Н.В. Клиначев

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

Рабочие файлы: [Шаг 1: ШИМ-драйвер] [Шаг 2: ШИМ +АЦП +ПДП] [Шаг 3: Регуляторы тока]
[Шаг 4: Вкл. мК и Мост] [Шаг 5: ARM PMSM FOC] [Шаг 6: ARM СДПМ + СКВТ]
[Настройка част. преобр.] [MOTOR_KOLESO] [SGGT-v1] [ДВМ100]
[по Табл.] [IQsin] [IQatan2] [TI C2000 PMSM FOC-control]

В приложении приведены листинги файлов полного программного кода бездатчиковой векторной системы управления для синхронного двигателя с возбуждением от постоянных магнитов. Бездатчиковая система управления обычно используется во время разработки и тестирования привода, когда полного комплекта оборудования в собранном состоянии еще нет (датчиков скорости, положения, редуктора, механической нагрузки и пр.). Поэтому программный код спроектирован таким образом, чтобы у инженера предприятия была возможность за 30 секунд выполнить изменения в программе, необходимые для подключения любого двигателя, к любому инвертеру, которые предприятие использует или производит.

Структура программного кода и замечания к построению проекта

Программный код векторной системы управления для синхронного двигателя с возбуждением от постоянных магнитов представлен в листинге файла main.c. Стандартные (для систем управления электроприводом) математические преобразования определены в форме макросов в библиотеке mclib.h и mclib.c. Макросы используются с целю достижения предельного быстродействия. Программа дополнена тремя базами данных. В первой определены параметры электродвигателей (файл motors.h). Во второй – параметры инвертеров – силовых полупроводниковых мостов с предрайверами и датчиками (файл mckits.h). В третьей определен программный код ШИМ-драйверов силового трехфазного моста специфичный для заданного микроконтроллера (файлы mcu-s.h и mcu-s.c).

В каждой базе инженеру необходимо раскомментировать определение каталожного идентификатора двигателя, инвертера и микроконтроллера соответственно. Или определить собственный, дополнив базу по образцу. База данных ШИМ-драйверов содержит шаблон драйвера (программный интерфейс). Если не определить каталожный идентификатор микроконтроллера, то активируется код интерфейса и любой Си-компилятор должен без ошибок выполнить построение проекта, поскольку основная программа векторной системы управления ни одной сторонней библиотеки не использует.

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

Микроконтроллер TMS320F28027F самый младший в семействе. И разработан был уже очень давно. Использовать в учебном процессе его категорически нельзя (много второстепенных нюансов). Но это хороший пример решения задачи даунгрейда при фиксировании конечного решения для серии. Младший в семействе – значит дешевый. Имеет минимальный размер ПЗУ и ОЗУ – значит кристалл маленький, а радиационная стойкость выше. Но инженеру необходимо решать ряд дополнительных задач. В частности необходимо разбираться и менять стандартную схему резервирования памяти для прошивки микроконтроллера. Поскольку для отладки программы в ОЗУ не хватает места. В виду обозначенного затруднения программа отлаживалась в стандартной конфигурации предполагающей прошивку флэш-памяти. Размер программы, в зависимости от микроконтроллера, составляет от 6 до 9 КБ. Еще одна особенность микроконтроллера фирмы ТИ в том, что необходимо планировать перемещение кода отдельных функций программы из флэш-памяти в ОЗУ после включения питания. Поскольку в ОЗУ код исполняется в 8..10 раз быстрее. Данные о времени исполнения главного блока программного кода приведены для микроконтроллеров в листинге файла main.c.

Алгоритм работы программы

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

Состояний – четыре. Первое. Двигатель выключен. Стойки моста не переключаются. Микроконтроллер измеряет смещение нуля датчиков тока. Второе состояние наступает при нажатии кнопки. Микроконтроллер запитывает обмотку статора номинальным током. Ротор двигателя (магнит) ориентируется по полю. Через временной интервал, в несколько раз больший, чем механическая постоянная времени, машина состояний переходит к состоянию за номером три. Микроконтроллер медленно поворачивает поле статора. Ротор поворачивается вслед за полем в синхронном режиме. Уставка скорости мала, поэтому система управления способствует гашению колебаний скорости ротора. Как только ротор пройдет 8 магнитных полюсов, т.е. по истечению временного интервала, машина состояний вновь перестраивает связи в системе управления и запускает двигатель в номинальном режиме. Из этого состояния (за номером четыре), при нажатии кнопки, программа переходит к состоянию за номером один.

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

Правила работы с отладчиком компилятора. Рекомендации

  1. Если система управления крутит двигатель, то отладчик компилятора (IAR) останавливать нельзя. Останавливать отладчик и менять параметры программы можно после выключения и остановки двигателя. При использовании CCS допустимо отслеживать и менять переменные в окне контрольных выражений (Watch window). Но отладчик реального времени должен быть переведен в низкоприоритетный режим.
  2. Для перевода параметров системы управления в абсолютные величины с плавающей точкой удобно использовать консоль отладчика браузера (F12), печатая выражения соответствующие шаблону: x / (1 << 24). Где x – целое число представляющее тип данных _iq24.
  3. При выполнении тюнинга системы управления следует помнить, что сравнительно безопасным является двукратное изменение любого параметра за исключением активного сопротивления секции статора и её индуктивности.
  4. Программа спроектирована для управления низковольтными инвертерами. Для вычислений выбран тип данных _iq24 (см. определение GL_Q в библиотеке mclib.h, листинг 3). Поэтому все координаты системы управления должны принадлежать диапазону от -128 до 127.999999940. В случае подключения платы с процессором к высоковольтному или к сильноточному инвертеру необходимо изменить тип данных.
  5. Если мощный моментный двигатель подключен к отключенному от источника питания низковольтному силовому мосту, то недопустимо вращать двигатель рукой – противо-ЭДС двигателя спалит силовой мост.

Уточнение параметров инвертера, электродвигателя и рабочего органа

  1. Измеряем смещение нуля датчиков тока. Включаем питание. Не нажимаем кнопку пуска двигателя. Стойки силового моста не переключаются. Датчики измеряют ток нулевой величины. Через 10..15 секунд устанавливаем точку остановки (break-point) на макросе ADC_OFFSET_MACRO. Измеренные величины (см. структуру McKit), переводим в число с плавающей точкой и фиксируем в записи об инвертере (редактируя поля CS_U0, CS_V0, CS_W0).
  2. Определяем число пар полюсов электродвигателя. Увеличиваем время работы в синхронном режиме до 100 сек (см. код задачи Task_motor_pos). Частоту тока в синхронном режиме устанавливаем равной 1 Гц (START_F). Подсчитываем количество оборотов. Фиксируем число полюсов машины.
  3. Уточняем константу противо-ЭДС машины (Ke). При включенном интегрирующем канале в регуляторе скорости, измеряем частоту токов в статоре. Если частота не соответствует уставке скорости, то меняем и фиксируем Ke. Настройка регулятора скорости может быть грубой: Kp может быть занижен, Ti – завышена (в 10 .. 100 раз больше электромагнитной постоянной времени).
  4. Измеряем приведенный к валу момент инерции. Отключаем код регулятора скорости. В регуляторе тока по моментной оси активируем канал комбинированного управления. Добавляем код (программного реле с положительным гистерезисом), меняющий знак уставки тока при достижении номинальной скорости. Включаем привод. Подсчитываем число реверсов в единицу времени. Производим вычисления и фиксируем приведенный к валу момент инерции.

Резюме по проекту системы управления для векторного электропривода на ARM-процессоре

В контексте решаемой задачи (система управления для электропривода) интегральное впечатление от ARM-процессора STM32F303xC / ядро Cortex-M4 / 72 МГц положительное. Не все модули процессора были использованы в проекте, но судя по задействованным, программистам производителя процессора удалось предложить единый шаблон конфигурирования аппаратной машины состояния процессора, что существенно облегчает работу конечного пользователя. Следует отметить аппаратную реализацию таймера (с шестью выходами), которая позволяет и трехфазный ШИ-модулятор реализовать, и шесть выходов сконфигурировать так, чтобы была возможность управлять трехфазным мостом с любым предрайвером (активные уровни, однотактный / двухтактный режимы, управление стойкой или транзисторами, бестоковая пауза, выключенное состояние).

Для реализации систем управления электропривода математический сопроцессор не нужен. Достаточно эмулировать операции с плавающей точкой на целочисленном АЛУ. При вычислении арктангенса необходимо нормировать сигналы – это единственное деление в программе. Целочисленное деление выполняется быстро. Точность результата на два бита меньше половины от разрядности АЛУ. Т.е. 14 бит для 32-х разрядных микроконтроллеров. Таким образом, 16-ти разрядный микроконтроллер – это неадекватный выбор аппаратной части для реализации современных систем управления электропривода.

ARM-процессор STM32F303xC выполняет оптимизированный код векторной системы за 14 мкс. Если силовой мост работает на частоте 20 кГц, то 3/4 времени процессор простаивает. Появляется желание выбрать менее мощный процессор не на 72, а на 36 МГц. Но первое, на что следует обратить внимание при выборе – это таймер, который может не иметь три канала захвата / сравнения оборудованные конфигурируемыми, комплементарными выходами да еще и с формированием бестоковой паузы. Точнее, кроме трех каналов, требуется и четвертый для управления процессом торможения двигателя.

Дальнейшая разработка программного кода

В листинге 5 программного кода файла main.c имеется несколько неиспользуемых функций (MODIFY_*_2_*), чей прототип объявлен и имеется реализация. Эти функции необходимы для реализации более совершенной машины состояний частотного преобразователя. Ссылка на соответствующую документацию в коде имеется. Фактически, предложен понятный план дальнейшей доработки программы. В текущем же состоянии – программа не сложная и базовую функциональность поддерживает. Когда машина состояний частотного преобразователя будет реализована, достаточно будет добавить 5 строк кода, которые изменят назначение программы. Под контролем модифицированного кода система управления переведет двигатель в режим генератора, а силовую часть – в режим бездатчикового синхронно-векторного выпрямителя (АВН).

Приложение. Листинги файлов исходного кода бездатчиковой векторной системы управления для синхронного двигателя с возбуждением от постоянных магнитов, реализованной на микроконтроллере TMS320F28027F или на ARM-процессоре с ядром Cortex-M4

Листинг 1. Файл mckits.h

/*************************************************************************
* File Name :   mckits.h
* Author :      Клиначев Николай Васильевич
* Version :     1.0.1
* Date :        20150707
* Description :
*      Ниже представлена база данных силовых инвертеров (MC KIT), которые
*      применялись при отладке программы векторной системы управления.
*      Для определения параметров инвертера, достаточно раскомментировать
*      определение его каталожного идентификатора в списке.
*************************************************************************/
//#define PRIVATE_KIT_1
#define PRIVATE_KIT_VG
//#define BOOSTXL_DRV8301
//#define DRV8312_C2_KIT

#if defined PRIVATE_KIT_1       // Самодельный MC KIT

#define BASE_VOLTAGE    25.0    // Напряжение dc-шины, соответствующие шкале АЦП, (В)
#define BASE_CURRENT    25.0    // Ток стойки, соответствующий половине шкалы АЦП, (А)
#define CS_U0           0.4105  // смещение нуля датчика тока фазы U (доля шкалы АЦП)
#define CS_V0           0.4124  // смещение нуля датчика тока фазы V (доля шкалы АЦП)
#define CS_W0           0.5000  // смещение нуля датчика тока фазы W (доля шкалы АЦП)
#define CS_DC0          0.0000  // смещение нуля датчика тока DC-шины (доля шкалы АЦП)
#define R_DC            0.175   // внутреннее сопротивление источника питания, (Ом)
#define SW_H_POLARITY   0       // Активный уровень предрайверов верхних ключей
#define SW_L_POLARITY   0       // Активный уровень предрайверов  нижних ключей
#define SW_H_IDLESTATE  1       // Уровень сигнала для верхних ключей в IDLE-режиме
#define SW_L_IDLESTATE  1       // Уровень сигнала для  нижних ключей в IDLE-режиме
#define MCU_OPEN_DRAIN  1       // 1, если питание у MCU и у Моста раздельное
#define SVPWM_ON        0       // 0, если мала полоса ОУ (не работает на ХХ)
#define PWM_FREQUENCY 17578.125 // Частота переключения стоек силового моста, (Гц)
#define PWM_DEADBAND    54      // Величина бестоковой паузы в циклах CPU, (Q0)

#elif defined PRIVATE_KIT_VG     // Самодельный MC KIT

#define BASE_VOLTAGE    30.0    // Напряжение dc-шины, соответствующие шкале АЦП, (В)
#define BASE_CURRENT    40.0    // Ток стойки, соответствующий половине шкалы АЦП, (А)
#define CS_U0           0.4949  // смещение нуля датчика тока фазы U (доля шкалы АЦП)
#define CS_V0           0.4972  // смещение нуля датчика тока фазы V (доля шкалы АЦП)
#define CS_W0           0.5000  // смещение нуля датчика тока фазы W (доля шкалы АЦП)
#define CS_DC0          0.0000  // смещение нуля датчика тока DC-шины (доля шкалы АЦП)
#define ES_A0           0.0260  // смещение нуля датчика противо-ЭДС Alpha-фазы
#define ES_B0           0.0110  // смещение нуля датчика противо-ЭДС Beta-фазы
#define R_DC            0.005   // внутреннее сопротивление источника питания, (Ом)
#define SW_H_POLARITY   0       // Активный уровень предрайверов верхних ключей
#define SW_L_POLARITY   0       // Активный уровень предрайверов  нижних ключей
#define SW_H_IDLESTATE  0       // Уровень сигнала для верхних ключей в IDLE-режиме
#define SW_L_IDLESTATE  0       // Уровень сигнала для  нижних ключей в IDLE-режиме
#define MCU_OPEN_DRAIN  0       // 1, если питание у MCU и у Моста раздельное
#define SVPWM_ON        0       // 0, если мала полоса ОУ (не работает на ХХ)
#define PWM_FREQUENCY   20000   // Частота переключения стоек силового моста, (Гц)
#define PWM_DEADBAND    60      // Величина бестоковой паузы в циклах CPU, (Q0)

#elif defined BOOSTXL_DRV8301   // MC KIT производства Texas Instruments

#define BASE_VOLTAGE    26.3    // Напряжение dc-шины, соответствующие шкале АЦП, (В)
#define BASE_CURRENT    16.5    // Ток стойки, соответствующий половине шкалы АЦП, (А)
#define CS_U0           0.5294  // смещение нуля датчика тока фазы U (доля шкалы АЦП)
#define CS_V0           0.5330  // смещение нуля датчика тока фазы V (доля шкалы АЦП)
#define CS_W0           0.5383  // смещение нуля датчика тока фазы W (доля шкалы АЦП)
#define CS_DC0          0.0000  // смещение нуля датчика тока DC-шины (доля шкалы АЦП)
#define R_DC            0.175   // внутреннее сопротивление источника питания, (Ом)
#define SW_H_POLARITY   1       // Активный уровень предрайверов верхних ключей
#define SW_L_POLARITY   1       // Активный уровень предрайверов  нижних ключей
#define SW_H_IDLESTATE  0       // Уровень сигнала для верхних ключей в IDLE-режиме
#define SW_L_IDLESTATE  0       // Уровень сигнала для  нижних ключей в IDLE-режиме
#define MCU_OPEN_DRAIN  0       // 1, если питание у MCU и у Моста раздельное
#define SVPWM_ON        0       // 0, если мала полоса ОУ (не работает на ХХ)
#define PWM_FREQUENCY   20000   // Частота переключения стоек силового моста, (Гц)
#define PWM_DEADBAND    30      // Величина бестоковой паузы в циклах CPU, (Q0)

#elif defined DRV8312_C2_KIT    // MC KIT производства Texas Instruments

#define BASE_VOLTAGE    66.32   // Напряжение dc-шины, соответствующие шкале АЦП, (В)
#define BASE_CURRENT    8.6     // Ток стойки, соответствующий половине шкалы АЦП, (А)
// ...

#endif

Листинг 2. Файл motors.h

/*************************************************************************
* File Name :   motors.h
* Author :      Клиначев Николай Васильевич
* Version :     1.0.1
* Date :        20150707
* Description :
*      Ниже представлена база данных электродвигателей, для которых была
*      настроена программа, реализующая векторную систему управления.
*      В контексте определения паспорта двигателя (#if defined MOTOR_ID)
*      приводятся контекст инвертера, к которому двигатель подключался.
*      Один двигатель может быть подключен к разным инвертерам (MC KIT).
*      Поэтому, в контексте двигателя, списков параметров системы
*      управления может быть несколько.
*      Для определения параметров системы управления, достаточно
*      раскомментировать определение каталожного идентификатора
*      одного двигателя из списка.
*************************************************************************/
//#define FL42BLS01
#define FL42BLS02               // близкий аналог DT4260_24_055_04
//#define BLY172S_24V_4000        // близкий аналог DT4260-24-055-04
//#define DN4261_24_053           // близкий аналог DT4260-24-055-04
//#define DT4260_24_055_04
//#define ECMA_C10604GS           // Delta Electronics 0.4 кВт 3000 об/мин
//#define MOTOR_LIFT
//#define MOTOR_V1
//#define SGGT_01
//#define MOTOR_KOLESO            // ОАО "Миассэлектроаппарат"
//#define DBU_70_70_1D2           // ОАО "Миассэлектроаппарат"
//#define DB72_40_1000            // ОАО "Миассэлектроаппарат"
//#define DVM100_021              // "Конструкторское бюро мехатроники"
//#define DVM100_12               // "Конструкторское бюро мехатроники"

/*************************************************************************
* SAFE_I – это такая уставка по току, при которой ротор (маховик),
*          не может накапливать энергию быстро, и при которой, система
*          управления не может быстро сбросить энергию в источник.
*          Лабораторные источники постоянного тока не поддерживают реверс
*          энергии. Если машина переходит в генераторный режим или в режим
*          рекуперативного торможения (при реверсе), то потенциал шины
*          постоянного тока увеличивается, у источника срабатывает защита.
*          В типовом случае, силовая часть MC_KIT не имеет балластного
*          резистора.
* BASE_  - это префикс, для физических величин, которые используются для
*          приведения координат системы управления к относительным
*          величинам, (pu), [-1.0, +1.0]. Например, при уставке SAFE_I,
*          абсолютная величина тока равна: SAFE_I * BASE_CURRENT
* KE
*          Внимание! Для определения константы продиво-ЭДС используют разные
*          формулы. Здесь константа противо-ЭДС связывает номинальную угловую
*          скорость вала (рад/с) и амплитуду противо-ЭДС фазы двигателя, чьи
*          секции соединены в звезду. Грубая транспортировка машины может
*          быть причиной снижения Ke до 5%. Для тюнинга Ke следует задать
*          скорость вала, вычислить соответствующую ей частоту тока в статоре
*          и добиться совпадения. В регуляторе скорости должен быть включен
*          интегрирующий канал. Альтернативно, можно контролировать модуль
*          потокосцепления - амплитуда psi.a должна быть равна 1.
*
* Начальное значение KKmC = 0.1. Максимальное - 1.
*          Тюнинг выполняется по уровню шума на выходе регулятора тока.
*
* Начальное значение WF = 20
*          Тюнинг выполнять без нагрузки. При уставке скорости равной 0.25.
*          Отслеживать осциллограммы потокосцепления и угла. Значение
*          сопрягающей частоты необходимо уменьшать до тех пор, пока смещения
*          нуля датчиков тока и непостоянство зазора под полюсами не начнут
*          искажать осциллограмму угла. Значение WF = 7 .. 15 признается хорошим.
*          http://model.exponenta.ru/k2/Jigrein/JS/fwlink.htm#B0BB   ф-ла (9)
*
* Порядок настройки регулятора скорости:
*          1. Комментируем строку   #define KP_S
*          2. Определяем            #define TI_S   0
*          3. Проверяем качество работы, подстраиваем значение: pid_spd.Kp
*          4. Фиксируем             #define KP_S   (pid_spd.Kp / (1 << GL_Q))
*          5. Комментируем строку   #define TI_S
*          6. Проверяем качество работы, подстраиваем значение: pid_spd.Ki
*          7. Фиксируем             #define TI_S   (KP_S / KI_S * dT)
*
*          Если Tм > 3 * Tя, то настройки регулятора, которые вычисляет
*          программа должны подойти. Привод сразу должен работать качественно.
*          Переопределение KP_S и TI_S может потребоваться, если у привода
*          меняется приведенный к валу момент инерции.
*
* Внимание!
*          Текущей версии программы могут не подходить настройки для
*          некоторых машин и устаревших силовых мостов.
*          Устаревшие параметры: BASE_FREQ, WL, KK_C.
*************************************************************************/

#if defined DVM100_021
/*** www.kbm36.ru ********************************************************
*                                                                Таблица 1
+------------------+---+----+--------+-------+----------+----------------+
|     Тип СДПМ     | m | Zp |   Ke   |   J   | L_ф  R_ф | I_фm  Mн   Pн  |
|                  | Y        В/рад/с  kg*m^2   mH   Om     A   Nm    W  |
|    ДВМ100.021    | 3   13   0.618     2e-3   2.5  1.28  2.5  2.31  45  |
+------------------+---+----+--------+-------+----------+----------------+
Kt = 1.5 * Ke,   Mн = I_фm * Kt,   omega_н = Pн / Mн,   Eфm = omega_н * Ke

*                                                                Таблица 2
+------------------+--------+--------+-------+----------+----------------+
|     Экв. ДПТ     |   E_я  |   KФ   |   J   | L_я  R_я |  Iн   Mн   Pн  |
|     условие:     |    В     В/рад/с  kg*m^2   mH   Om     A   Nm    W  |
|     Eя = Eфm     |  15.6    0.618     2e-3  1.67 0.853  3.75 2.31  45  |
+------------------+--------+--------+-------+----------+----------------+
*************************************************************************/
#define BASE_FREQ       39      // Максимальная частота фазных токов, (Гц)
#define POLES           26      // Число полюсов = 2 * Zp
#define KE      0.617           // Y Константа противо-ЭДС секции, (В/(рад/сек))
#define RS      (2.615 / 2)     // Y активное сопротивление секции статора, (Ом)
#define LS      (0.002 / 2)     // Y индуктивность секции статора, (Гн)
#define IS      2.5             // Y максимальный ток секции статора, (А)
#define JPR     (2e-3 + 0)      // Приведенный к ротору момент инерции, (кг * м^2)
//
// Параметры векторной системы управления для данного СДПМ и инвертера
//
#if defined PRIVATE_KIT_1       // Самодельный MC KIT
#define SAFE_S  0.5             // 0 или Уставка по скорости, [-1.0, 1.0]
#define SAFE_I  0.04            // Безопасная уставка по току, [0.0, 1.0]
#define KK_C    0.8             // Коэффициент усиления контура тока 0.5 .. 4
#define KP_S    0.15            // Коэффициент П-канала регулятора скорости
#define TI_S    8e-3            // 0 или Постоянная времени И-канала рег. скорости
#define WL      5               // Весовой коэффициент предела ошибки рег. скорости
#define WF      30              // Сопр. частота фильтров в наб. потокосцеплений, (рад/с)
#define START_F 6               // Частота фазных токов в синхронном режиме запуска, (Гц)
#elif defined BOOSTXL_DRV8301   // MC KIT производства Texas Instruments
// ...
#elif defined DRV8312_C2_KIT    // MC KIT производства Texas Instruments
// ...
#endif

#elif defined(DT4260_24_055_04) || defined(DN4261_24_053) \
     || defined(FL42BLS02) || defined(BLY172S_24V_4000)
/*************************************************************************
*                                                                Таблица 1
+------------------+---+----+--------+-------+----------+----------------+
|     Тип СДПМ     | m | Zp |   Ke   |   J   | L_ф  R_ф | I_фm  Mн   Pн  |
|                  | Y        В/рад/с  kg*m^2   mH   Om     A   Nm    W  |
| DT4260-24-055-04 | 3    4   0.0224    4e-6   0.6 0.483  3.9  .131  55  |
+------------------+---+----+--------+-------+----------+----------------+
Kt = 1.5 * Ke,   Mн = I_фm * Kt,   omega_н = Pн / Mн,   Eфm = omega_н * Ke

*                                                                Таблица 2
+------------------+--------+--------+-------+----------+----------------+
|     Экв. ДПТ     |   E_я  |   KФ   |   J   | L_я  R_я |  Iн   Mн   Pн  |
|     условие:     |    В     В/рад/с  kg*m^2   mH   Om     A   Nm    W  |
|     Eя = Eфm     |  9.41    0.0224    4e-6   0.4 0.322  5.85   -    -  |
+------------------+--------+--------+-------+----------+----------------+
Другие названия этой машины: DN4261-24-053, FL42BLS02, BLY172S-24V-4000
*************************************************************************/
#define BASE_FREQ       200     // Максимальная частота фазных токов, (Гц)
#define POLES           8       // Число полюсов = 2 * Zp
#define KE      0.02245         // Y Константа противо-ЭДС секции, (В/(рад/сек))
#define RS      (0.965 / 2)     // Y активное сопротивление секции статора, (Ом)
#define LS      (0.0012 / 2)    // Y индуктивность секции статора, (Гн)
#define IS      3.9             // Y максимальный ток секции статора, (А)
#define JPR     (4e-6 + 0e-6)   // Приведенный к ротору момент инерции, (кг * м^2)
//
// Параметры векторной системы управления для данного СДПМ и инвертера
//
#if defined PRIVATE_KIT_1       // Самодельный MC KIT
#define SAFE_S  0.5             // 0 или Уставка по скорости, [-1.0, 1.0]
#define SAFE_I  0.05            // Безопасная уставка по току, [0.0, 1.0]
#define KK_C    0.8             // Коэффициент усиления контура тока 0.5 .. 4
#define KP_S    0.4             // Коэффициент П-канала регулятора скорости
#define TI_S    0.006           // 0 или Постоянная времени И-канала рег. скорости
#define WL      2800            // Весовой коэффициент предела ошибки рег. скорости
#define WF      45              // Сопр. частота фильтров в наб. потокосцеплений, (рад/с)
#define START_F 9               // Частота фазных токов в синхронном режиме запуска, (Гц)
#elif defined PRIVATE_KIT_VG    // Самодельный MC KIT
#define SAFE_S  0.5             // 0 или Уставка по скорости, [-1.0, 1.0]
#define SAFE_I  0.03            // Безопасная уставка по току, [0.0, 1.0]
#define KKmC    0.7             // Вес для Коэффициента усиления контура тока < 1.0
#define KP_S    5.0             // Коэффициент П-канала регулятора скорости
#define TI_S    0.006           // 0 или Постоянная времени И-канала рег. скорости
#define LES     0.010           // Ограничение ошибки (по модулю) в И-канале: .4..10 %
#define WF      22              // Сопр. частота фильтров в наб. потокосцеплений, (рад/с)
#define START_F 9               // Частота фазных токов в синхронном режиме запуска, (Гц)
#elif defined BOOSTXL_DRV8301   // MC KIT производства Texas Instruments
#define SAFE_S  0.5             // 0 или Уставка по скорости, [-1.0, 1.0]
#define SAFE_I  0.05            // Безопасная уставка по току, [0.0, 1.0]
#define KK_C    0.8             // Коэффициент усиления контура тока 0.5 .. 4
#define KP_S    0.4             // Коэффициент П-канала регулятора скорости
#define TI_S    0.006           // 0 или Постоянная времени И-канала рег. скорости
#define WL      2800            // Весовой коэффициент предела ошибки рег. скорости
#define WF      45              // Сопр. частота фильтров в наб. потокосцеплений, (рад/с)
#define START_F 9               // Частота фазных токов в синхронном режиме запуска, (Гц)
#elif defined DRV8312_C2_KIT    // MC KIT производства Texas Instruments
// ...
#endif

#elif defined FL42BLS01
/*************************************************************************
*                                                                Таблица 1
+------------------+---+----+--------+-------+----------+----------------+
|     Тип СДПМ     | m | Zp |   Ke   |   J   | L_ф  R_ф | I_фm  Mн   Pн  |
|                  | Y        В/рад/с  kg*m^2   mH   Om     A   Nm    W  |
|     FL42BLS01    | 3    4   0.0185   2.4e-6  1.3  0.9   1.5  .062  26  |
+------------------+---+----+--------+-------+----------+----------------+
Kt = 1.5 * Ke,   Mн = I_фm * Kt,   omega_н = Pн / Mн,   Eфm = omega_н * Ke
*************************************************************************/
#define BASE_FREQ       250     // Максимальная частота фазных токов, (Гц)
#define POLES           8       // Число полюсов = 2 * Zp
#define KE      0.0185          // Y Константа противо-ЭДС секции, (В/(рад/сек))
#define RS      (1.8 / 2)       // Y активное сопротивление секции статора, (Ом)
#define LS      (0.0026 / 2)    // Y индуктивность секции статора, (Гн)
#define IS      1.5             // Y максимальный ток секции статора, (А)
#define JPR     (2.4e-6 * 1)    // Приведенный к ротору момент инерции, (кг * м^2)
//
// Параметры векторной системы управления для данного СДПМ и инвертера
//
#if defined PRIVATE_KIT_1       // Самодельный MC KIT
#define SAFE_S  0.4             // 0 или Уставка по скорости, [-1.0, 1.0]
#define SAFE_I  0.04            // Безопасная уставка по току, [0.0, 1.0]
#define KK_C    1.0             // Коэффициент усиления контура тока 0.5 .. 4
#define KP_S    0.15            // Коэффициент П-канала регулятора скорости
#define TI_S    0.008           // 0 или Постоянная времени И-канала рег. скорости
#define WL      2800            // Весовой коэффициент предела ошибки рег. скорости
#define WF      45              // Сопр. частота фильтров в наб. потокосцеплений, (рад/с)
#define START_F 20              // Частота фазных токов в синхронном режиме запуска, (Гц)
#elif defined BOOSTXL_DRV8301   // MC KIT производства Texas Instruments
// ...
#elif defined DRV8312_C2_KIT    // MC KIT производства Texas Instruments
// ...
#endif

#elif defined ECMA_C10604GS
/*************************************************************************
*                                                                Таблица 1
+------------------+---+----+--------+-------+----------+----------------+
|     Тип СДПМ     | m | Zp |   Ke   |   J   | L_ф  R_ф | I_фm  Mн   Pн  |
|                  | Y        В/рад/с  kg*m^2   mH   Om     A   Nm   kW  |
|   ECMA_C10604GS  | 3    5    0.25   27.7e-6  5.0  1.7   3.7  1.27  0.4 |
+------------------+---+----+--------+-------+----------+----------------+
Kt = 1.5 * Ke,   Mн = I_фm * Kt,   omega_н = Pн / Mн,   Eфm = omega_н * Ke
*************************************************************************/
#define BASE_FREQ       250     // Максимальная частота фазных токов, (Гц)
#define POLES           10      // Число полюсов = 2 * Zp
#define KE      0.25            // Y Константа противо-ЭДС секции, (В/(рад/сек))
#define RS      (3.4 / 2)       // Y активное сопротивление секции статора, (Ом)
#define LS      (0.01 / 2)      // Y индуктивность секции статора, (Гн)
#define IS      3.7             // Y максимальный ток секции статора, (А)
#define JPR     (27.7e-6 * 1)   // Приведенный к ротору момент инерции, (кг * м^2)
//
// Параметры векторной системы управления для данного СДПМ и инвертера
//
#if defined PRIVATE_KIT_1       // Самодельный MC KIT
#define SAFE_S  0.1             // 0 или Уставка по скорости, [-1.0, 1.0]
#define SAFE_I  0.04            // Безопасная уставка по току, [0.0, 1.0]
#define KK_C    1.0             // Коэффициент усиления контура тока 0.5 .. 4
#define KP_S    2               // Коэффициент П-канала регулятора скорости
#define TI_S    0.06            // 0 или Постоянная времени И-канала рег. скорости
#define WL      6000            // Весовой коэффициент предела ошибки рег. скорости
#define WF      25              // Сопр. частота фильтров в наб. потокосцеплений, (рад/с)
#define START_F 6               // Частота фазных токов в синхронном режиме запуска, (Гц)
#elif defined BOOSTXL_DRV8301   // MC KIT производства Texas Instruments
// ...
#elif defined DRV8312_C2_KIT    // MC KIT производства Texas Instruments
// ...
#endif

#elif defined MOTOR_KOLESO
/*************************************************************************
*                                                                Таблица 1
+------------------+---+----+--------+-------+----------+----------------+
|     Тип СДПМ     | m | Zp |   Ke   |   J   | L_ф  R_ф | I_фm  Mн   Pн  |
|                  | Y        В/рад/с  kg*m^2   mH   Om     A   Nm   kW  |
|   MOTOR_KOLESO   | 3   14    0.87     0.29   8.25 0.75   20   26       |
+------------------+---+----+--------+-------+----------+----------------+
Kt = 1.5 * Ke,   Mн = I_фm * Kt,   omega_н = Pн / Mн,   Eфm = omega_н * Ke
*************************************************************************/
#define BASE_FREQ      100      // Максимальная частота фазных токов, (Гц)
#define POLES           28      // Число полюсов = 2 * Zp
#define KE      0.87            // Y Константа противо-ЭДС секции, (В/(рад/сек))
#define RS      (1.5 / 2)       // Y активное сопротивление секции статора, (Ом)
#define LS      (0.0165 / 2)    // Y индуктивность секции статора, (Гн)
#define IS      20              // Y максимальный ток секции статора, (А)
#define JPR     0.29            // Приведенный к ротору момент инерции, (кг * м^2)
//
// Параметры векторной системы управления для данного СДПМ и инвертера
//
#if defined PRIVATE_KIT_1       // Самодельный MC KIT
#define SAFE_S  0.2             // 0 или Уставка по скорости, [-1.0, 1.0]
#define SAFE_I  0.1             // Безопасная уставка по току, [0.0, 1.0]
#define KK_C    1.0             // Коэффициент усиления контура тока 0.5 .. 4
#define KP_S    8               // Коэффициент П-канала регулятора скорости
#define TI_S    0.06            // 0 или Постоянная времени И-канала рег. скорости
#define WL      6000            // Весовой коэффициент предела ошибки рег. скорости
#define WF      20              // Сопр. частота фильтров в наб. потокосцеплений, (рад/с)
#define START_F 1               // Частота фазных токов в синхронном режиме запуска, (Гц)
#elif defined PRIVATE_KIT_2     // Самодельный MC KIT
#define SAFE_S  0.2             // 0 или Уставка по скорости, [-1.0, 1.0]
#define SAFE_I  0.1             // Безопасная уставка по току, [0.0, 1.0]
#define KK_C    1.0             // Коэффициент усиления контура тока 0.5 .. 4
#define KP_S    8               // Коэффициент П-канала регулятора скорости
#define TI_S    0.06            // 0 или Постоянная времени И-канала рег. скорости
#define WL      6000            // Весовой коэффициент предела ошибки рег. скорости
#define WF      20              // Сопр. частота фильтров в наб. потокосцеплений, (рад/с)
#define START_F 1               // Частота фазных токов в синхронном режиме запуска, (Гц)
#elif defined PRIVATE_KIT_3     // Самодельный MC KIT
#define SAFE_S  0.2             // 0 или Уставка по скорости, [-1.0, 1.0]
#define SAFE_I  0.1             // Безопасная уставка по току, [0.0, 1.0]
#define KK_C    1.0             // Коэффициент усиления контура тока 0.5 .. 4
#define KP_S    8               // Коэффициент П-канала регулятора скорости
#define TI_S    0.06            // 0 или Постоянная времени И-канала рег. скорости
#define WL      6000            // Весовой коэффициент предела ошибки рег. скорости
#define WF      20              // Сопр. частота фильтров в наб. потокосцеплений, (рад/с)
#define START_F 1               // Частота фазных токов в синхронном режиме запуска, (Гц)
#elif defined BOOSTXL_DRV8301   // MC KIT производства Texas Instruments
#define SAFE_S  0.1             // 0 или Уставка по скорости, [-1.0, 1.0]
#define SAFE_I  0.1             // Безопасная уставка по току, [0.0, 1.0]
#define KK_C    1.0             // Коэффициент усиления контура тока 0.5 .. 4
#define KP_S    4               // Коэффициент П-канала регулятора скорости
#define TI_S    0.06            // 0 или Постоянная времени И-канала рег. скорости
#define WL      6000            // Весовой коэффициент предела ошибки рег. скорости
#define WF      20              // Сопр. частота фильтров в наб. потокосцеплений, (рад/с)
#define START_F 1               // Частота фазных токов в синхронном режиме запуска, (Гц)
#elif defined PRIVATE_KIT_VG    // Самодельный MC KIT
#define SAFE_S  0.5             // 0 или Уставка по скорости, [-1.0, 1.0]
#define SAFE_I  0.04            // Безопасная уставка по току, [0.0, 1.0]
#define KKmC    0.10            // Вес для Коэффициента усиления контура тока < 1.0
#define KP_S    8               // Коэффициент П-канала регулятора скорости
#define TI_S    0.06            // 0 или Постоянная времени И-канала рег. скорости
#define LES     0.014           // Ограничение ошибки (по модулю) в И-канале: .4..10 %
#define WF      12              // Сопр. частота фильтров в наб. потокосцеплений, (рад/с)
#define START_F 4               // Частота фазных токов в синхронном режиме запуска, (Гц)
#elif defined DRV8312_C2_KIT    // MC KIT производства Texas Instruments
// ...
#endif

#elif defined MOTOR_LIFT
/*************************************************************************
*                                                                Таблица 1
+------------------+---+----+--------+-------+----------+----------------+
|     Тип СДПМ     | m | Zp |   Ke   |   J   | L_ф  R_ф | I_фm  Mн   Pн  |
|                  | Y        В/рад/с  kg*m^2   mH   Om     A   Nm   kW  |
|    MOTOR_LIFT    | 3   16    35.7             80  5.6   10.6  570  2k7 |
+------------------+---+----+--------+-------+----------+----------------+
Kt = 1.5 * Ke,   Mн = I_фm * Kt,   omega_н = Pн / Mн,   Eфm = omega_н * Ke
*************************************************************************/
#define BASE_FREQ       12      // Максимальная частота фазных токов, (Гц)
#define POLES           32      // Число полюсов = 2 * Zp
#define KE      35.7            // Y Константа противо-ЭДС секции, (В/(рад/сек))
#define RS      5.6             // Y активное сопротивление секции статора, (Ом)
#define LS      0.08            // Y индуктивность секции статора, (Гн)
#define IS      10.6            // Y максимальный ток секции статора, (А)
#define JPR     0               // Приведенный к ротору момент инерции, (кг * м^2)
//
// Параметры векторной системы управления для данного СДПМ и инвертера
//
#if defined PRIVATE_KIT_3       // Самодельный MC KIT
#define SAFE_S  0.04            // 0 или Уставка по скорости, [-1.0, 1.0]
#define SAFE_I  0.15            // Безопасная уставка по току, [0.0, 1.0]
#define KK_C    0.25            // Коэффициент усиления контура тока 0.5 .. 4
#define KP_S    2.0             // Коэффициент П-канала регулятора скорости
#define TI_S    0.12            // 0 или Постоянная времени И-канала рег. скорости
#define WL      3000            // Весовой коэффициент предела ошибки рег. скорости
#define WF      1.5             // Сопр. частота фильтров в наб. потокосцеплений, (рад/с)
#define START_F 0.3             // Частота фазных токов в синхронном режиме запуска, (Гц)
#endif

#elif defined MOTOR_V1
#define BASE_FREQ       50      // Максимальная частота фазных токов, (Гц)
#define POLES           34      // Число полюсов = 2 * Zp
#define KE      2.66            // Y Константа противо-ЭДС секции, (В/(рад/сек))
#define RS      (5e-3 * 9 +0.05)// Y активное сопротивление секции статора, (Ом)
#define LS      (60e-6 * 9)     // Y индуктивность секции статора, (Гн)
#define IS      400.0           // Y максимальный ток секции статора, (А)
#define JPR     (0.336 + 0)     // Приведенный к ротору момент инерции, (кг * м^2)
//
// Параметры векторной системы управления для данного СДПМ и инвертера
//
#if defined PRIVATE_KIT_1       // Самодельный MC KIT
#define SAFE_S  0.2             // 0 или Уставка по скорости, [-1.0, 1.0]
#define SAFE_I  0.07            // Безопасная уставка по току, [0.0, 1.0]
#define KK_C    1.0             // Коэффициент усиления контура тока 0.5 .. 4
#define KP_S    2.4             // Коэффициент П-канала регулятора скорости
#define TI_S    0.08            // 0 или Постоянная времени И-канала рег. скорости
#define WL      3000            // Весовой коэффициент предела ошибки рег. скорости
#define WF      5               // Сопр. частота фильтров в наб. потокосцеплений, (рад/с)
#define START_F 1               // Частота фазных токов в синхронном режиме запуска, (Гц)
#elif defined BOOSTXL_DRV8301   // MC KIT производства Texas Instruments
// ...
#elif defined DRV8312_C2_KIT    // MC KIT производства Texas Instruments
// ...
#endif

#elif defined DBU_70_70_1D2     // ОАО "Миассэлектроаппарат"
/*************************************************************************
*                                                                Таблица 1
+------------------+---+----+--------+-------+----------+----------------+
|     Тип СДПМ     | m | Zp |   Ke   |   J   | L_ф  R_ф | I_фm  Mн   Pн  |
|                  | Y        В/рад/с  kg*m^2   mH  mOm     A   Nm    W  |
|  ДБУ 70-70-1.2   | 3    2    0.059     -     1.0  250                  |
+------------------+---+----+--------+-------+----------+----------------+
Kt = 1.5 * Ke,   Mн = I_фm * Kt,   omega_н = Pн / Mн,   Eфm = omega_н * Ke
*************************************************************************/
#define POLES           4       // Число полюсов = 2 * Zp
#define KE      0.059           // Y Константа противо-ЭДС секции, (В/(рад/сек))
#define RS      0.25            // Y активное сопротивление секции статора, (Ом)
#define LS      0.001           // Y индуктивность секции статора, (Гн)
#define IS      5.0             // Y максимальный ток секции статора, (А)
//#define JPR     (0)           // Приведенный к ротору момент инерции, (кг * м^2)
//
// Параметры векторной системы управления для данного СДПМ и инвертера
//
#if defined PRIVATE_KIT_VG      // Самодельный MC KIT
#define SAFE_S  0.5             // 0 или Уставка по скорости, [-1.0, 1.0]
#define SAFE_I  0.03            // Безопасная уставка по току, [0.0, 1.0]
#define KKmC    0.7             // Вес для Коэффициента усиления контура тока < 1.0
#define KP_S    5.0             // Коэффициент П-канала регулятора скорости
#define TI_S    0.006           // 0 или Постоянная времени И-канала рег. скорости
#define LES     0.010           // Ограничение ошибки (по модулю) в И-канале: .4..10 %
#define WF      18              // Сопр. частота фильтров в наб. потокосцеплений, (рад/с)
#define START_F 5               // Частота фазных токов в синхронном режиме запуска, (Гц)
#endif

#elif defined DB72_40_1000     // ОАО "Миассэлектроаппарат"
/*************************************************************************
*                                                                Таблица 1
+------------------+---+----+--------+-------+----------+----------------+
|     Тип СДПМ     | m | Zp |   Ke   |   J   | L_ф  R_ф | I_фm  Mн   Pн  |
|                  | Y        В/рад/с  kg*m^2   mH   Om     A   Nm    W  |
|   ДБ72-40-1000   | 3   10    0.118     -     2.2  1.0                  |
+------------------+---+----+--------+-------+----------+----------------+
Kt = 1.5 * Ke,   Mн = I_фm * Kt,   omega_н = Pн / Mн,   Eфm = omega_н * Ke
*************************************************************************/
#define POLES          20       // Число полюсов = 2 * Zp
#define KE      0.118           // Y Константа противо-ЭДС секции, (В/(рад/сек))
#define RS      1.00            // Y активное сопротивление секции статора, (Ом)
#define LS      0.0022          // Y индуктивность секции статора, (Гн)
#define IS      3.0             // Y максимальный ток секции статора, (А)
#define JPR     0               // Приведенный к ротору момент инерции, (кг * м^2)
//
// Параметры векторной системы управления для данного СДПМ и инвертера
//
#if defined PRIVATE_KIT_VG      // Самодельный MC KIT
#define SAFE_S  0.8             // 0 или Уставка по скорости, [-1.0, 1.0]
#define SAFE_I  0.03            // Безопасная уставка по току, [0.0, 1.0]
#define KKmC    0.18            // Вес для Коэффициента усиления контура тока < 1.0
#define KP_S    7.0             // Коэффициент П-канала регулятора скорости
#define TI_S    0.018           // 0 или Постоянная времени И-канала рег. скорости
#define LES     0.010           // Ограничение ошибки (по модулю) в И-канале: .4..10 %
#define WF      25              // Сопр. частота фильтров в наб. потокосцеплений, (рад/с)
#define START_F 6               // Частота фазных токов в синхронном режиме запуска, (Гц)
#endif

#elif defined DVM100_12         // "Конструкторское бюро мехатроники" г. Златоуст
/*************************************************************************
*                                                                Таблица 1
+------------------+---+----+--------+-------+----------+----------------+
|     Тип СДПМ     | m | Zp |   Ke   |   J   | L_ф  R_ф | I_фm  Mн   Pн  |
|                  | Y        В/рад/с  kg*m^2   mH   Om     A   Nm    W  |
|    ДВМ100.12     | 3   13    0.70      -     5.0  1.5   2.5            |
+------------------+---+----+--------+-------+----------+----------------+
Kt = 1.5 * Ke,   Mн = I_фm * Kt,   omega_н = Pн / Mн,   Eфm = omega_н * Ke
*************************************************************************/
#define POLES          26       // Число полюсов = 2 * Zp
#define KE      0.70            // Y Константа противо-ЭДС секции, (В/(рад/сек))
#define RS      1.50            // Y активное сопротивление секции статора, (Ом)
#define LS      0.005           // Y индуктивность секции статора, (Гн)
#define IS      2.5             // Y максимальный ток секции статора, (А)
#define JPR     0.001           // Приведенный к ротору момент инерции, (кг * м^2)
//
// Параметры векторной системы управления для данного СДПМ и инвертера
//
#if defined PRIVATE_KIT_VG      // Самодельный MC KIT
#define SAFE_S  0.8             // 0 или Уставка по скорости, [-1.0, 1.0]
#define SAFE_I  0.03            // Безопасная уставка по току, [0.0, 1.0]
#define KKmC    0.1             // Вес для Коэффициента усиления контура тока < 1.0
#define KP_S    3.0             // Коэффициент П-канала регулятора скорости
#define TI_S    0.016           // 0 или Постоянная времени И-канала рег. скорости
#define LES     0.010           // Ограничение ошибки (по модулю) в И-канале: .4..10 %
#define WF      18              // Сопр. частота фильтров в наб. потокосцеплений, (рад/с)
#define START_F 5               // Частота фазных токов в синхронном режиме запуска, (Гц)
#endif

#endif

Листинг 3. Файл mclib.h

/******************************************************************************
* File Name :   mclib.h
* Author :      Клиначев Николай Васильевич
* Version :     1.1.0
* Date :        20150707
* Description : Библиотеки IQ-Math library и Motor Control library
*       Библиотека математических преобразователей, необходимых
*       при реализации цифровых систем управления электропривода
*       и необходимые макросы целочисленной арифметики (IQ-математика)
*       http://model.exponenta.ru/k2/Jigrein/JS/fwlink.htm#B447
*******************************************************************************/
/* Защита от рекурсивных подключений библиотеки-------------------------------*/
#ifndef __MCLIB_H
#define __MCLIB_H

/* Includes ------------------------------------------------------------------*/

/* Exported types ------------------------------------------------------------*/
// ******************************************************************************* //
typedef long    _iq;    // это _iq24, _iq22 или _iq20 в зависимости от GL_Q

// ******************************************************************************* //
// Измеряемые величины на силовом модуле
typedef struct
{
    _iq Vdc;        // Output: Измеренное напряжение DC-шины (сигнал ОС), [ 0.0, 1.0]
    _iq Idc;        // Output: Измеренный ток DC-шины (сигнал ОС),        [-1.0, 1.0]
    _iq uIs;        // Output: Измеренный ток фазы "U" статора,           [-1.0, 1.0]
    _iq vIs;        // Output: Измеренный ток фазы "V" статора,           [-1.0, 1.0]
    _iq uIs0;       // Output: Смещение нуля датчика тока фазы "U",       0.5 +/-10 %
    _iq vIs0;       // Output: Смещение нуля датчика тока фазы "V",       0.5 +/-10 %
    _iq Vm;         // Output: Максимальная амплитуда фазного напряжения, 1.0 | 1.155
    _iq Em;         // Output: Максимальная амплитуда фазной противо-ЭДС, 1.0 почти
}
MCKIT_TypeDef;

// ******************************************************************************* //
// Указатель на угловое положение вала, Запускающий Интегратор и пр.
typedef struct {
    _iq *yPhi;      // Input: Указатель на угловое положение вала
    _iq  Sine;      // Output: (8000..FFFF-0000..7FFF) << (GL_Q - 15)
    _iq  Cosine;    // Output: (8000..FFFF-0000..7FFF) << (GL_Q - 15)
    _iq  gS;        // Output: Уставка Скорости в режиме запуска, [-1.0, 1.0]
    _iq  zo;        // sPrivate: Частота тока   в режиме запуска, [ 1/z,  Гц]
    _iq  phi;       // Output: Угловое положение ротора,       [-M_PI, +M_PI]
}
ROTOR_TypeDef;

// ******************************************************************************* //
// Инверсный преобразователь Парка
typedef struct {
    _iq *d;         // Input: Вещественная составляющая координаты (по оси d)
    _iq *q;         // Input: Мнимая составляющая координаты (по оси q)
    _iq *Sine;      // Input: Указатель на опорный синусоидальный сигнал (pu)
    _iq *Cosine;    // Input: Указатель на опорный косинусоидальный сигнал (pu)
    _iq  a;         // Output: Alpha-фаза вращающейся координаты (сдвиг - 0 градусов)
    _iq  b;         // Output: Beta-фаза вращающейся координаты (сдвиг - 90 градусов)
}
PIPARK_TypeDef;

// ******************************************************************************* //
// Преобразователь числа фаз (двухфазной системы в трехфазную)
typedef struct {
    _iq *a;         // Input: Alpha-фаза вращающейся координаты (сдвиг - 0 градусов)
    _iq *b;         // Input: Beta-фаза вращающейся координаты (сдвиг - 90 градусов)
    _iq  u;         // Output: U-фаза трехфазной системы UVW
    _iq  v;         // Output: V-фаза трехфазной системы UVW
    _iq  w;         // Output: W-фаза трехфазной системы UVW
}
PICLARKE_TypeDef;

// ******************************************************************************* //
// Преобразователь числа фаз (трехфазной системы в двухфазную)
typedef struct
{
    _iq *u;         // Input:  Ток фазы "U" статора (UVW  - индексы трехфазной системы)
    _iq *v;         // Input:  Ток фазы "V" статора (UVW  - индексы трехфазной системы)
    _iq *w;         // Input:  Ток фазы "W" статора (UVW  - индексы трехфазной системы)
    _iq  a;         // Output: Ток фазы "a" статора ("Alpha/Beta" - 2-х фазная система)
    _iq  b;         // Output: Ток фазы "b" статора ("Alpha/Beta" - 2-х фазная система)
}
PCLARKE_TypeDef;

// ******************************************************************************* //
// Преобразователь Парка
typedef struct
{
    _iq *a;         // Input: Alpha-фаза вращающейся координаты (сдвиг - 0 градусов)
    _iq *b;         // Input: Beta-фаза вращающейся координаты (сдвиг - 90 градусов)
    _iq *Sine;      // Input: Указатель на опорный синусоидальный сигнал (pu)
    _iq *Cosine;    // Input: Указатель на опорный косинусоидальный сигнал (pu)
    _iq  d;         // Output: Вещественная составляющая координаты (по оси d)
    _iq  q;         // Output: Мнимая       составляющая координаты (по оси q)
}
PPARK_TypeDef;

// ******************************************************************************* //
// ПИ-регулятор контура тока (с каналом комбинированного управления)
typedef struct
{
    _iq *gI;        // Input: Указатель на Уставку (задание) для РТ, [-1.0, 1.0]
    _iq *yI;        // Input: Указатель на сигнал ОС с датчика тока, [-1.0, 1.0]
    _iq *gC;        // Input: Указатель на сигнал канала комб. упр., [-1.0, 1.0]
    _iq  zo;        // sPrivate: Выход регистра задержки интегратора
    _iq  uV;        // Output:   Выход РТ (скважность для ШИМ),      [-1.0, 1.0]
    _iq  Kp;        // Const: Коэффициент усиления пропорционального канала РТ
    _iq  Ki;        // Const: Коэффициент усиления интегрирующего    канала РТ
}
DQ_PID_TypeDef;

#define DQ_PID_MACRO(ir)                                                    \
{                                                                           \
    _iq ir_xI;                             /* Ошибка регулятора тока      */\
    _iq ir_zi;                             /* Вход регистра задержки      */\
    ir_xI  = *ir.gI - *ir.yI;              /* Ошибка регулятора           */\
    ir.uV  = _IQmpy(ir_xI, ir.Kp);         /*   безынерционный канал      */\
    ir.uV +=  ir.zo;                       /* + интегрирующий  канал      */\
    ir.uV += *ir.gC;                       /* + канал комбин. управления  */\
    ir_zi  = _IQmpy(ir_xI, ir.Ki);         /* РT:Ki                       */\
    ir_zi +=  ir.zo;                       /* 1/s: Значение на входе      */\
    /* ---------------------------------------------------                */\
    ir.zo  =  ir_zi;                                                        \
    ir.zo  =  ir.zo < _IQ(-1.15 * 2) ? _IQ(-1.15 * 2) :      /* SVM 2 180 */\
              ir.zo > _IQ(+1.15 * 2) ? _IQ(+1.15 * 2) : ir.zo;              \
}

// ******************************************************************************* //
// Регулятор Скорости c Ограничителем
typedef struct
{
    _iq *gI;  // Input: Указатель на Уставку Момента  (предел по току), [ 0.0, 1.0]
    _iq *gS;  // Input: Указатель на Уставку Скорости (задание для РС), [-1.0, 1.0]
    _iq *yS;  // Input: Указатель на скорость вала  (сигнал ОС для РС), [-1.0, 1.0]
    _iq zo;   // sPrivate: Выход регистра задержки интегратора
    _iq uS;   // Output: Выход РС (задание с ограничением для РТ),      [-1.0, 1.0]
    _iq Kp;   // Const: Коэффициент усиления пропорционального канала РС
    _iq Ki;   // Const: Коэффициент усиления интегрирующего    канала РС
    _iq Lx;   // Const: Предел ограничения сигнала на входе И-канала
}
SPD_PID_TypeDef;

#define SPD_PID_MACRO(sr)                                                     \
{                                                                             \
    _iq sr_gI = *sr.gI;                      /* Предел по току, [ 0.0, 1.0] */\
    _iq sr_xS = *sr.gS - *sr.yS;             /* Ошибка регулятора скорости  */\
    sr.uS = _IQmpy(sr.Kp, sr_gI);            /* Kp * gI  ...                */\
    sr.uS = _IQmpy(sr.uS, sr_xS);            /*  ...  безынерционный канал  */\
    sr.uS = sr.uS + sr.zo;                   /* Выход  регулятора скорости  */\
    sr.uS = sr.uS < -sr_gI ? -sr_gI : sr.uS > +sr_gI ? +sr_gI : sr.uS;        \
    /* ---------------------------------------------------                  */\
    sr_xS = sr_xS < -sr.Lx ? -sr.Lx : sr_xS > +sr.Lx ? +sr.Lx : sr_xS;        \
    sr.zo = sr.zo + _IQmpy(sr_xS, _IQmpy(sr.Ki, sr_gI));   /* Интегратор РС */\
    sr.zo = sr.zo < -sr_gI ? -sr_gI : sr.zo > +sr_gI ? +sr_gI : sr.zo;        \
}

// ******************************************************************************* //
// Наблюдатель Скорости вала СДПМ
typedef struct
{
    _iq *Vm;  // Input: Указатель max амплитуду фазного напряжения,   [0.0, 1.155]
    _iq *Vq;  // Input: Указатель на текущую скважность ШИМ (Duty),   [-1.0, +1.0]
    _iq *Iq;  // Input: Указатель на сигнал ОС с датчика тока,        [-1.0, +1.0]
    _iq  zo;  // sPrivate: Выход регистра задержки апериодического звена
    _iq  yS;  // Output: Скорость = (Uq / (1 + Te s) - Iq * Rs) / Ke, [-1.0, +1.0]
    _iq  yS09;// Output: se.yS09 = 0.9 * se.yS - масштабирование для подхвата
}
SPD_ESTIMATOR_TypeDef;

#define SPD_ESTIMATOR_MACRO(se)           /*    Вычисления в отн. ед.  :-)    */\
{                                                                               \
    _iq se_zi;                /* Вход регистра задержки апериодического звена */\
    se_zi = _IQmpy(*se.Vm, *se.Vq);       /* Получили Vq из скважности, (pu)  */\
    se_zi = _IQmpy(se_zi - se.zo, _IQ(TIMESTEP / (LS / RS))) + se.zo;   /* zi */\
    se.yS = se_zi - _IQmpy(*se.Iq,        /*  Противо-ЭДС (b-EMF), (pu)       */\
            _IQ(BASE_CURRENT * (RS + R_DC / 2.0) / (BASE_VOLTAGE / 2.0)));      \
    /* ---------------------------------------------------------------------- */\
    se.zo = se_zi;                                                              \
}

// ******************************************************************************* //
// Наблюдатель потокосцеплений
typedef struct
{
    _iq *Vm;        // Input: Указатель max амплитуду фазного напряжения, (pu)
    _iq *Va;        // Input: Alpha-фаза вращающейся координаты (сдвиг - 0 градусов)
    _iq *Vb;        // Input: Beta-фаза вращающейся координаты (сдвиг - 90 градусов)
    _iq *Ia;        // Input: Alpha-фаза вращающейся координаты (сдвиг - 0 градусов)
    _iq *Ib;        // Input: Beta-фаза вращающейся координаты (сдвиг - 90 градусов)
    _iq zoA;        // sPrivate: Выход регистра задержки
    _iq zoB;        // sPrivate: Выход регистра задержки
    _iq a;          // Output: Потокосцепление Alpha-фазы
    _iq b;          // Output: Потокосцепление Beta-фазы
    _iq phi;        // Output: Угловое положение ротора, [-M_PI, +M_PI]
}
PSI_ESTIMATOR_TypeDef;

/*******************************************************************************
* И входные и выходные координаты приведены к относительным величинам  (pu).
* U, в зависимости от вида модуляции: [-1.0 .. +1.0] или [-1.155 .. +1.155].
* Для приведения потокосцепления используется базовая величина - BASE_OMEGA.
*       (BASE_VOLTAGE / 2.0) / (KE / (POLES / 2.0)) = (BASE_OMEGA)
*       (KE / (POLES / 2.0)) / (BASE_VOLTAGE / 2.0) = (1 / BASE_OMEGA)
*******************************************************************************/
#define PSI_ESTIMATOR_MACRO(psi)                                                \
{                                                                               \
    _iq psi_ziA               /* Вход регистра задержки апериодического звена */\
             = _IQmpy(*psi.Va,  *psi.Vm)   /* Получили Va из скважности, (pu) */\
             - _IQmpy(*psi.Ia,  _IQ(BASE_CURRENT * RS / (BASE_VOLTAGE / 2.0)))  \
             - _IQmpy(psi.zoA,  _IQ(WF                /  BASE_OMEGA));          \
    psi_ziA  = _IQmpy(psi_ziA,  _IQ(TIMESTEP          *  BASE_OMEGA));          \
    psi.a    =        psi.zoA + _IQdiv2(psi_ziA)                                \
             - _IQmpy(*psi.Ia,  _IQ(BASE_CURRENT * LS / (KE / (POLES / 2.0)))); \
    /* ---------------------------------------------------------------------- */\
    psi.zoA += psi_ziA;                                                         \
                                                                                \
    _iq psi_ziB               /* Вход регистра задержки апериодического звена */\
             = _IQmpy(*psi.Vb,  *psi.Vm)   /* Получили Vb из скважности, (pu) */\
             - _IQmpy(*psi.Ib,  _IQ(BASE_CURRENT * RS / (BASE_VOLTAGE / 2.0)))  \
             - _IQmpy(psi.zoB,  _IQ(WF                /  BASE_OMEGA));          \
    psi_ziB  = _IQmpy(psi_ziB,  _IQ(TIMESTEP          *  BASE_OMEGA));          \
    psi.b    =        psi.zoB + _IQdiv2(psi_ziB)                                \
             - _IQmpy(*psi.Ib,  _IQ(BASE_CURRENT * LS / (KE / (POLES / 2.0)))); \
    /* ---------------------------------------------------------------------- */\
    psi.zoB += psi_ziB;                                                         \
}

// ******************************************************************************* //
// Следящий за вектором потокосцепления контур ФАПЧ: 1/2 Преобразователя Парка
// + ПИ-регулятор (с каналом комбинированного управления) + Интегратор (ГУН)
typedef struct
{
    _iq *a;         // Input: Alpha-фаза вращающейся координаты (сдвиг - 0 градусов)
    _iq *b;         // Input: Beta-фаза вращающейся координаты (сдвиг - 90 градусов)
    _iq *yS;        // Input: Указатель на сигнал ОС контура скорости,       [-1.0, 1.0]
    _iq d;          // Private: Составляющая координаты по оси d (потокосцепление)
    _iq q;          // Private: Составляющая координаты по оси q (ошибка конт. ФАПЧ)
    _iq zo;         // sPrivate: Выход регистра задержки интегратора PI-регулятора
    _iq uV;         // Private: Управляющее значение для ГУН-а (для 1s),     [-1.0, 1.0]
    _iq Kp;         // Const: Коэффициент усиления пропорционального канала PI-регулятора
    _iq Ki;         // Const: Коэффициент усиления интегрирующего канала PI-регулятора
    _iq phi;        // Output: Угловое положение ротора, (1/z),           [-M_PI, +M_PI]
    _iq Sine;       // Output: Соответствующее углу значение   синуса, (pu), [-1.0, 1.0]
    _iq Cosine;     // Output: Соответствующее углу значение косинуса, (pu), [-1.0, 1.0]
    _iq Spd;        // Output: Скорость вала          = uV * 1/(1+Ts), (pu), [-1.0, 1.0]
    _iq Psi;        // Output: Модуль потокосцепления  = d * 1/(1+Ts), (pu),  1.0 +/-5 %
    unsigned short  CapState;     // Критерй состояния захвата
    signed   short  CapCntr;      // Насыщающийся счётчик времени захвата
}
PLL_TypeDef;

// Макрос фильтра скорости для выведения на рабочий режим во время захвата
// интегратора наблюдателя скорости эквивалентного ДПТ
//
#define PLL_SPD_MACRO(PLL)                                                      \
{                                                                               \
    PLL.Spd += _IQmpy(PLL.uV - PLL.Spd, _IQ(WF * TIMESTEP));      /* 1/(1+Ts) */\
}

// Макрос критерия захвата / потери вектора потокосцепления контуром ФАПЧ
//
#define PLL_CAP_MACRO(PLL)                                                      \
{                                                                               \
    /* Вычислим |Psi| - проекцию отслеживаемого вектора на ось d, = 1 +/-0.18 */\
    PLL.d    = _IQmpy(*PLL.a, PLL.Cosine) + _IQmpy(*PLL.b, PLL.Sine);           \
    _iq dPsi =  PLL.d - PLL.Psi;   /* Отклонение Psi от усредненного значения */\
    PLL.Psi += _IQmpy(dPsi, _IQ(WF * TIMESTEP));           /* Усреденение Psi */\
    /* Проверим модуль вектора потокосцепления и ..                           */\
    if (PLL.Psi < _IQ(0.59)) { PLL.CapState = 0; }                              \
    else if ((dPsi < _IQ(-0.15) || _IQ(0.15) < dPsi))   /* .. коридор захвата */\
    { PLL.CapState = 1; }     /* Сброс насыщающегося счётчика времени захвата */\
}

// Макрос следящего за вектором потокосцепления контура ФАПЧ
//
#define PLL_MACRO(PLL)                                                          \
{                                                                               \
    _iq PLA;                                   /* для макросов _IQsin и _IQcos*/\
    PLL.Sine   = _IQsin(PLL.phi, PLA);                                          \
    PLL.Cosine = _IQcos(PLL.phi, PLA);                                          \
    /* ---------------------------------------------------------------------- */\
    PLL.q = _IQmpy(*PLL.b, PLL.Cosine) - _IQmpy(*PLL.a, PLL.Sine);              \
    /* Защита от перегрузки во время захвата (не более X0 mS).                */\
    if (_IQ(-0.18) < PLL.q && PLL.q < _IQ(+0.18))/*  ограничивает V слежения! */\
    { PLL.q <<= 4; }                            /*   учли масштаб Kp, Ki      */\
    /* ---------------------------------------------------------------------- */\
    PLL.uV   = _IQmpy(PLL.q, PLL.Kp);          /*   безынерционный канал      */\
    PLL.uV  +=  PLL.zo;                        /* + интегрирующий  канал      */\
    PLL.uV  += *PLL.yS;                        /* + канал комбин. управления  */\
    PLL.zo  += _IQmpy(PLL.q, PLL.Ki);          /*   ERK11, [-1.0, 1.0] + шум  */\
    /* Защита интегратора от перегрузки во время захвата (не более X0 mS).    */\
    PLL.zo  = PLL.zo < _IQ(-2.4) ? _IQ(-2.4) :  /*   Предел позволяет следить */\
              PLL.zo > _IQ(+2.4) ? _IQ(+2.4) : PLL.zo;    /* до скорости 240% */\
    /* ---------------------------------------------------------------------- */\
    PLL.phi += _IQmpy(PLL.uV, _IQ(BASE_OMEGA * TIMESTEP));                      \
    if (PLL.phi > +_IQ(M_PI)) PLL.phi -= _IQ(2 * M_PI);                         \
    if (PLL.phi < -_IQ(M_PI)) PLL.phi += _IQ(2 * M_PI);                         \
}

#define COMBINED_SVM_MACRO(ab2uvw)    /* (c) Клиначев Николай Васильевич */                                                              \
{                                                                                                                                        \
    _iq ab2uvw_u, ab2uvw_v, ab2uvw_w;                                                                                                    \
    if (ab2uvw.u < ab2uvw.v) {                                                                                                           \
        if (ab2uvw.u < ab2uvw.w) {                                                                                                       \
            ab2uvw_v = ab2uvw.v - (_IQ(1.0) + ab2uvw.u);                                                                                 \
            ab2uvw_w = ab2uvw.w - (_IQ(1.0) + ab2uvw.u);                                                                                 \
            if      (ab2uvw_v > _IQ(1.0)) { ab2uvw.w -= ab2uvw.w < 0 ? ab2uvw.v + _IQ(-1.0) : _IQ(1.0) + ab2uvw.u; ab2uvw.v = _IQ(1.0); }\
            else if (ab2uvw_w > _IQ(1.0)) { ab2uvw.v -= ab2uvw.v < 0 ? ab2uvw.w + _IQ(-1.0) : _IQ(1.0) + ab2uvw.u; ab2uvw.w = _IQ(1.0); }\
            else                          { ab2uvw.v = ab2uvw_v; ab2uvw.w = ab2uvw_w;                                                   }\
            ab2uvw.u = _IQ(-1.0);         /* U-фаза, 2 сектора, 60 + 60 градусов                                                       */\
        } else {                                                                                                                         \
            ab2uvw_u = ab2uvw.u - (_IQ(1.0) + ab2uvw.w);                                                                                 \
            ab2uvw_v = ab2uvw.v - (_IQ(1.0) + ab2uvw.w);                                                                                 \
            if      (ab2uvw_v > _IQ(1.0)) { ab2uvw.u -= ab2uvw.u < 0 ? ab2uvw.v + _IQ(-1.0) : _IQ(1.0) + ab2uvw.w; ab2uvw.v = _IQ(1.0); }\
            else                          { ab2uvw.u = ab2uvw_u; ab2uvw.v = ab2uvw_v;                                                   }\
            ab2uvw.w = _IQ(-1.0);         /* W-фаза, сектор №1, 60 градусов                                                            */\
        }                                                                                                                                \
    } else if (ab2uvw.v < ab2uvw.w) {                                                                                                    \
            ab2uvw_w = ab2uvw.w - (_IQ(1.0) + ab2uvw.v);                                                                                 \
            ab2uvw_u = ab2uvw.u - (_IQ(1.0) + ab2uvw.v);                                                                                 \
            if      (ab2uvw_w > _IQ(1.0)) { ab2uvw.u -= ab2uvw.u < 0 ? ab2uvw.w + _IQ(-1.0) : _IQ(1.0) + ab2uvw.v; ab2uvw.w = _IQ(1.0); }\
            else if (ab2uvw_u > _IQ(1.0)) { ab2uvw.w -= ab2uvw.w < 0 ? ab2uvw.u + _IQ(-1.0) : _IQ(1.0) + ab2uvw.v; ab2uvw.u = _IQ(1.0); }\
            else                          { ab2uvw.w = ab2uvw_w; ab2uvw.u = ab2uvw_u;                                                   }\
            ab2uvw.v = _IQ(-1.0);         /* V-фаза, 2 сектора, 60 + 60 градусов                                                       */\
    } else if (ab2uvw.w < ab2uvw.v) {                                                                                                    \
            ab2uvw_u = ab2uvw.u - (_IQ(1.0) + ab2uvw.w);                                                                                 \
            ab2uvw_v = ab2uvw.v - (_IQ(1.0) + ab2uvw.w);                                                                                 \
            if      (ab2uvw_u > _IQ(1.0)) { ab2uvw.v -= ab2uvw.v < 0 ? ab2uvw.u + _IQ(-1.0) : _IQ(1.0) + ab2uvw.w; ab2uvw.u = _IQ(1.0); }\
            else                          { ab2uvw.u = ab2uvw_u; ab2uvw.v = ab2uvw_v;                                                   }\
            ab2uvw.w = _IQ(-1.0);         /* W-фаза, сектор №2, 60 градусов                                                            */\
    } else                                { ab2uvw.u = _IQ(-1.0); ab2uvw.v = _IQ(-1.0); ab2uvw.w = _IQ(-1.0); }                          \
}

/* Exported constants --------------------------------------------------------*/
/* Exported macro ------------------------------------------------------------*/
#ifndef GL_Q            // Точность типа данных для IQ-вычислений: 20 22 24
#define GL_Q 20         // Определять здесь. В motors.h много мороки
#endif
#if GL_Q == 24          // _iq24:   -128 .. +127.999 999 940    0.000 000 060
#define _IQ(A)          (long) ((A) * 16777216.0L)      // _IQ24
#elif GL_Q == 22        // _iq22:   -512 .. +511.999 999 76     0.000 000 24
#define _IQ(A)          (long) ((A) * 4194304.0L)       // _IQ22
#elif GL_Q == 20        // _iq20:  -2048 .. 2047.999 999 0      0.000 001
#define _IQ(A)          (long) ((A) * 1048576.0L)       // _IQ20
#else
#error "PMSM: Wrong IQ type. Use: IQ24 or IQ22 or IQ20"
#endif                  // Если в motors.h, то так надо все IQ-макросы

// _iq15:    -1 .. +1 - 0.000030517578125
#define _IQ15(A)        (s16) ((A) * 32768.0L)
/*******************************************************************************
* Description :  Макрос преобразования биполярного значения
*                к униполярному с масштабированием
* Input : x    - число в формате _Q15: 8000..FFFF-0000..7FFF
* Param : BASE - униполярное число: 0000..FFFF, на 1 большее MAX значения
* Return :       0x0000 .. (base - 1)
*/
#define _Q15toBASE(x, BASE) ((unsigned long) ((  signed short) (x) + 0x8000)   \
                                           * ((unsigned short) (BASE)) >> 16)
#define _IQtoIQ15(A)        ((long) (A) >> (GL_Q - 15))
#define _IQdiv2(A)          ((A) >> 1)
#define _IQmpy(A, B)        (long) (((long long) (A) * (long long) (B)) >> GL_Q)
#define _IQmpy2(A)          ((A) << 1)

/*******************************************************************************
* Macros Name :   _IQsin
* Description :   Возвращает полиномиальное приближение функции sin
*     Тип данных аргумента и возвращаемого значения - GL_Q.
*     Второй аргумент - вспомогательная переменная - GL_Q.
*     Точность результата зависит от количества членов ряда:
*     1) x * (0.98557 - x * x * 0.142595)
*                           абсолютная погрешность менее 0.0045
*     2) x * (0.9996951 - x * x * (0.16567 - x * x * 0.0075132))
*                           абсолютная погрешность менее 68e-6
*******************************************************************************/
#define _IQsin(PHI_EXPR, VAR) (                                                 \
    VAR = (PHI_EXPR),                                                           \
    VAR = VAR > +_IQ(M_PI / 2) ? -VAR + _IQ(M_PI)                               \
        : VAR < -_IQ(M_PI / 2) ? -VAR - _IQ(M_PI) : VAR,                        \
    _IQmpy(VAR, (_IQ(0.98557) - _IQmpy(VAR, _IQmpy(VAR, _IQ(0.142595)))))       \
)

/*******************************************************************************
* Macros Name :   _IQsin
* Description :   Возвращает полиномиальное приближение функции cos
*     Тип данных аргумента и возвращаемого значения - GL_Q.
*     Второй аргумент - вспомогательная переменная - GL_Q.
*     Точность результата зависит от количества членов ряда:
*     1) x * (0.98557 - x * x * 0.142595)
*                           абсолютная погрешность менее 0.0045
*     2) x * (0.9996951 - x * x * (0.16567 - x * x * 0.0075132))
*                           абсолютная погрешность менее 68e-6
*******************************************************************************/
#define _IQcos(PHI_EXPR, VAR) (                                                 \
    VAR = (PHI_EXPR),                                                           \
    VAR = (VAR > 0 ? -VAR : VAR) + _IQ(M_PI / 2),                               \
    _IQmpy(VAR, (_IQ(0.98557) - _IQmpy(VAR, _IQmpy(VAR, _IQ(0.142595)))))       \
)

/* Exported functions ------------------------------------------------------- */
// Укажем компилятору, что функции библиотеки должны запускаться из ОЗУ ********
//#ifdef LAUNCHXL_F28027F // эх ...
#pragma CODE_SECTION(_IQatan2,              "ramfuncs");
#pragma CODE_SECTION(_IQdiv14BIT,           "ramfuncs");
#pragma CODE_SECTION(_IQatan,               "ramfuncs");
//#endif // линковщик CCS не понимает здесь

_iq _IQatan2(_iq A, _iq B);

#endif /* __MCLIB_H */

Листинг 4. Файл mclib.c

/******************************************************************************
* File Name :   mclib.c
* Author :      Клиначев Николай Васильевич
* Version :     1.1.0
* Date :        20150707
* Description : Библиотеки IQ-Math library и Motor Control library
*       Библиотека математических преобразователей, необходимых
*       при реализации цифровых систем управления электропривода
*       и необходимые макросы целочисленной арифметики (IQ-математика)
*******************************************************************************/
#include "mclib.h"

#define M_PI            3.141592653589793       // Число Пи
#define M_PI2           (M_PI / 2)              // Число Пи / 2

/*******************************************************************************
* Function Name : _IQdiv14BIT
* Description :   Функция деления. Делит первый аргумент на второй.
*     Аргументы - 32-х битные целые числа со знаком.
*     Тип данных аргументов и возвращаемого значения - GL_Q.
*     Точность результата - 14 бит (30 / 2).
*     При одновременном уменьшении значений аргументов - не падает.
*     Время исполнения от значений аргументов не зависит - 1.1 uS.
*******************************************************************************/
static inline _iq _IQdiv14BIT(_iq A, _iq B) {
    _iq x = A > 0 ? A : -A;
    _iq y = B > 0 ? B : -B;
    y = x > y ? x : y;
    unsigned char i, j;
    if (y & 0xFFFF8000U) {
        if (y & 0xFF800000U) {
            if (y & 0xF8000000U) {
                i = y & 0xE0000000U ? 0 : 2;
            } else {
                i = y & 0xFE000000U ? 4 : 6;
            }
        } else {
            if (y & 0xFFF80000U) {
                i = y & 0xFFE00000U ? 8 : 10;
            } else {
                i = y & 0xFFFE0000U ? 12 : 14;
            }
        }
    } else {
        if (y & 0xFFFFFF80U) {
            if (y & 0xFFFFF800U) {
                i = y & 0xFFFFE000U ? 30 - 14 : 30 - 12;
            } else {
                i = y & 0xFFFFFE00U ? 30 - 10 : 30 - 8;
            }
        } else {
            if (y & 0xFFFFFFF8U) {
                i = y & 0xFFFFFFE0U ? 30 - 6 : 30 - 4;
            } else {
                i = y & 0xFFFFFFFEU ? 30 - 2 : 30 - 0;
            }
        }
    }
    j = i >> 1;
    j = i <= GL_Q ? (GL_Q >> 1) - j : j - (GL_Q >> 1);
    return
    i <= GL_Q
        ? ((long)((long)(A << i) / (B >> j)) << j)
        : ((long)((long)(A << i) / (B << j)) >> j);
}

/*******************************************************************************
* Function Name : _IQatan
* Description :   Базовая функция вычисления значения арктангенса полиномом.
*     Аргумент и возвращаемое значение - 32-х битные целые числа со знаком.
*     Тип данных аргумента и возвращаемого значения - GL_Q.
*     Диапазон значений для аргумента: от _IQ(0.0) до _IQ(1.0)
*     Абсолютная погрешность вычисления результата - 0.0035 рад.
*     Время исполнения от значений аргументов не зависит - #.# uS.
*******************************************************************************/
static inline _iq _IQatan(_iq A) {
    return _IQmpy(A, (_IQ(1.054) - _IQmpy(_IQ(0.265), A))); // 0.42 uS.
    // Формула, имеющая абсолютную погрешность 0.00134 рад.
    //return _IQmpy(A, (_IQ(1.0268) - _IQmpy(A,
    //    (_IQmpy(A, _IQ(0.0779)) + _IQ(0.1649))))); // 0.58 uS.
    // Формула, имеющая абсолютную погрешность 0.000113 рад.
    //return
    //    _IQmpy(A, (_IQmpy(A, (_IQmpy(A, (_IQmpy(A, _IQ(0.14142))
    //    - _IQ(0.34523))) - _IQ(0.01306))) + _IQ(1.00241))); // 0.77 uS.
    // x * (1.0268 - x * (x * 0.0779 + 0.1649))
    // x * (x * (x * (x * 0.14142 - 0.34523) - 0.01306) + 1.00241)
}

/*******************************************************************************
* Function Name : _IQatan2
* Description :   Функция вычисления значения арктангенса _IQatan2(Y / X).
*     Аргументы и возвращаемое значение - 32-х битные целые числа со знаком.
*     Тип данных аргумента и возвращаемого значения - GL_Q.
*     Диапазон значений для аргументов: весь диапазон типа данных.
*     Абсолютная погрешность вычисления результата - 0.0035 рад.
*     Время исполнения от значений аргументов не зависит - 2.7 .. 2.9 uS.
*******************************************************************************/
_iq _IQatan2(_iq A, _iq B) {
    if (A == 0) { return B >= 0 ?          0 : -_IQ(M_PI ); }
    if (B == 0) { return A >  0 ? _IQ(M_PI2) : -_IQ(M_PI2); }
    if (B >  0) {
        if (A > 0) {
            return B > A ? +_IQatan(_IQdiv14BIT(A, B))
                         : -_IQatan(_IQdiv14BIT(B, A)) + _IQ(M_PI2);
        } else {
            A = -A;
            return B > A ? -_IQatan(_IQdiv14BIT(A, B))
                         : +_IQatan(_IQdiv14BIT(B, A)) - _IQ(M_PI2);
        }
    } else {
        B = -B;
        if (A > 0) {
            return B > A ? -_IQatan(_IQdiv14BIT(A, B)) + _IQ(M_PI)
                         : +_IQatan(_IQdiv14BIT(B, A)) + _IQ(M_PI2);
        } else {
            A = -A;
            return B > A ? +_IQatan(_IQdiv14BIT(A, B)) - _IQ(M_PI)
                         : -_IQatan(_IQdiv14BIT(B, A)) - _IQ(M_PI2);
        }
    }
}

Листинг 5. Файл main.c

/******************************************************************************
* File Name :   main.c
* Author :      Клиначев Николай Васильевич
* Version :     1.1.0
* Date :        20150707
* Target :      Любой 32-битный микроконтроллер с motor-control-периферией
*               от 48 МГц, FPU:off/none, см. уточнение в mcu-s.h
* Description : Программа векторной бездатчиковой системы управления для
*               синхронного двигателя с возбуждением от постоянных магнитов
*               СДПМ (PMSM / BLAC).
*     Программа реализует подчиненную управляющую систему, которая позволяет
*     контролировать момент и угловую скорость вала синхронного двигателя с
*     постоянными магнитами (СДПМ / PMSM) во всех четырех квадрантах плоскости
*     механической характеристики. Тип управления – векторный, бездатчиковый.
*     Скорость вала вычисляется по напряжению DC-шины и мгновенным значениям
*     токов фаз двигателя. Положение ротора - с помощью вычислителя мгновенных
*     значений потокосцеплений по осям Alpha/Beta.
*     ШИМ-драйвер поддерживает и классическую и векторную модуляцию.
*     Сторонние библиотеки цифровой обработки сигналов не используются.
*     Для эмуляции выполнения операций с плавающей точкой на целочисленном
*     АЛУ определены макросы IQ-математики. При адаптации к высоковольтным
*     инвертерам или к инвертерам на большие токи потребуется смена типа
*     данных. Текущий тип данных, _iq24, поддерживает числа в диапазоне:
*     от -128 до 127.999999940, с разрешением 0.000000060.
*
*     Соглашение о префиксах для координат САР:
*          g – задающее воздействие (Ref),
*          y – регулируемая координата (Fbk),
*          x – ошибка регулирования (Err),
*          u – управление на объект (Out),
*          p – верхний предел координаты (upper, pozitive),
*          n – нижний предел координаты (lower, negative),
*          zo# – значение хранящееся в регистре задержки (1/z),
*          zi# – значение на входе регистра задержки (input of 1/z),
*          u# – внутренняя координата регулятора (private coordinate).
*
*     Алгоритм работы с отладчиком:
*          1. Запустить отладчик в свободном режиме
*          2. Нажать кнопку для запуска двигателя
*          3. Нажать кнопку для остановки двигателя
*          4. Остановить отладчик
*          5. Изменить значения переменных в Watch window
*          6. Перейти к шагу 1
*******************************************************************************/
// TODO: Реализовать измерение напряжения шины постоянного тока McKit.Vdc
// TODO: Реализовать управление для ключа балластного резистора
// TODO: Реализовать возбуждение и обработку сигналов резольвера
// TODO: Определить уровни компиляции проекта для поэтапной настройки привода
// Сомнительные идеи:
// TODO: Реализовать выбор 2х фаз для измерения тока (особенно при SVPWM)
// TODO: Низкая частота вращения - ШИ-модуляция, высокая - SV-коммутация
// TODO: Увеличить период дискретизации контура тока CURRENT_LOOP_TIMESTEP
// TODO: Увеличить период дискретизации контура скорости SPEED_LOOP_TIMESTEP

/* Includes ------------------------------------------------------------------*/
#include "mckits.h"     // Подключаем базу с параметрами силовых мостов
#include "motors.h"     // Подключаем базу с параметрами электродвигателей
#include "mcu-s.h"      // Подключаем базу данных плат микроконтроллеров
#include "mclib.h"      // Подключаем библиотеки IQ-Math и Motor Control

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
#define M_PI            3.141592653589793       // Число Пи
#define M_SQRT3         1.7320508075688772      // Math.sqrt(3)

// Период вызова главного прерывания программы - MainISR.
// Определяет шаг дискретизации ЦСУ == ISR_PRESCALER * (1 / PWM_FREQUENCY), (сек)
#define TIMESTEP        ((((unsigned int)(CPU_SYSCLK / PWM_FREQUENCY)) \
                              & 0xfffe) / CPU_SYSCLK * ISR_PRESCALER)
#define CURRENT_LOOP_TIMESTEP  (TIMESTEP * 1)   // Шаг дискретизации РТ ЦСУ (сек)
#define SPEED_LOOP_TIMESTEP    (TIMESTEP * 1)   // Шаг дискретизации РC ЦСУ (сек)
#define PWM_RESOLUTION  (((unsigned int)(CPU_SYSCLK / PWM_FREQUENCY)) >> 1)
#define FSM_TIMESTEP    (((unsigned int)(0.02 / TIMESTEP)) * TIMESTEP)

// Ограниченная напряжением Udc максимальная частота токов статора, (рад/с)
#define BASE_OMEGA      ((BASE_VOLTAGE / 2.0) / (KE / (POLES / 2.0)))

// Коэффициент P-канала регулятора скорости
#if !defined KP_S
#if defined IS_SENS
#define KP_S            8.0     // С датчиком и минимальным моментом инерции
#else
#define KP_S            2.0     // т.к. система боле чувствительна к помехам
#endif
#endif  // Без интегрирующего канала коэффициент ошибки c0 = 1 / (1 + KP_S)

// Коэффициент усиления контура тока: Kкт = Tя / dT
#if !defined KK_C
#if !defined KKmC
#define KK_C            ((LS / RS) / CURRENT_LOOP_TIMESTEP * 0.18)
#else
#define KK_C            ((LS / RS) / CURRENT_LOOP_TIMESTEP * KKmC)
#endif
#endif
// Коэффициент передачи преобразователя напряжения (ШИМ-драйвера)
// или Максимально возможное напряжение на секции двигателя, (Y), KPWM == V_MAX
#define KPWM            (SVPWM_ON ? BASE_VOLTAGE / M_SQRT3 : BASE_VOLTAGE / 2.0)
// Коэффициент P-канала регулятора тока: Kрт = Kкт Rя / Kдт / Kпн
#define KP_C            (KK_C * RS / (1.0 / BASE_CURRENT) / KPWM)
#define TI_C            (LS / RS)       // Электромагнитная постоянная времени

#if !defined TI_S
// Электромагнитная постоянная времени
#define TE              (LS / RS)
// Механическая постоянная времени: Tм = Rя J / (KФ)^2
#define TM              (JPR == 0 ? 0 : ((2.0 / 3 * RS) * JPR / (3.0 / 2 * KE * KE)))
// Постоянная времени И-канала регулятора скорости: Tрс = Tм .. (4..6 Tя)
#define TI_S            (TM < 2 * TE ? 0 : TM < 6 * TE ? TM : (6 * TE))
//
// Если TI_S == 0, то механическая постоянная времени машины меньше
// электромагнитной. В этом случае необходимо подключить к ротору момент
// инерции нагрузки. Проконтролировать условие TI_S == 0 можно в функции:
// PMSM_CntrlUnit_SetBlkParam, - отслеживая переменную pid_spd.Ki
//
#warning "PMSM: IF (TM < 2 * TE) THEN { enlarge JPR in motors.h }"
#endif

// Частота фазных токов в синхронном режиме запуска, (Гц)
#if !defined START_F
#define START_F            (1.5 * WF / (2 * M_PI))
#endif


/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
// http://model.exponenta.ru/k2/Jigrein/JS/fwlink.htm#A2F2
typedef enum {
  OFF = 0, // Частотный преобразователь выключен
  POS = 1, // Режим позиционирования / торможения
  SIX = 2, // Режим синхронного запуска
  CAP = 3, // Режим захвата
  RUN = 5  // Режим штатного функционирования
} STATE_TypeDef;
STATE_TypeDef State = OFF;

// Глобальные уставки Цифровой системы управления для СДПМ (PMSM / BLAC)
typedef struct {
    _iq gU;         // Output: Уставка Напряжения в отн. ед.    (pu), [-1.0, +1.0]
    _iq gS;         // Output: Уставка Скорости   в отн. ед.    (pu), [-1.0, +1.0]
    _iq gI;         // Output: Уставка тока по оси момента, Iq, (pu), [ 0.0, +1.0]
    _iq g0;         // Output: Уставка тока по оси    поля, Id, (pu). 4 PMSM = 0.0
    _iq GND;        // Const: 0
    unsigned short iR;  // Индекс координат выводимых в ЦАП в рабочем режиме
    unsigned short iC;  // Индекс координат выводимых в ЦАП в режиме захвата
    FunctionalState SVPWM_Enable; // Output: флаг ативизации SVPWM
} SETPOINTS_TypeDef;
// Глобальные уставки Цифровой системы управления для СДПМ (PMSM / BLAC)
SETPOINTS_TypeDef       SetPnt;
// Интегратор Угла и другие координаты, связанные с положением ротора
ROTOR_TypeDef           Rotor;
// Инверсный преобразователь Парка (dq2ab == dc2ac)
PIPARK_TypeDef          dq2ab;
// Инвертирующий преобразователь Кларка (преобразователь числа фаз - 2Ph_2_3Ph)
PICLARKE_TypeDef        ab2uvw;
// Неинверти-щий преобразователь Кларка (преобразователь числа фаз - 3Ph_2_2Ph)
PCLARKE_TypeDef         uvw2ab;
// Прямой преобразователь Парка (ab2dq == ac2dc)
PPARK_TypeDef           ab2dq;
// Подчиненный PI-регулятор q-проекции тока статора СДПМ (ось момента)
DQ_PID_TypeDef          pid_iq;
// Подчиненный PI-регулятор d-проекции тока статора СДПМ (ось поля)
DQ_PID_TypeDef          pid_id;
// Наблюдатель потокосцеплений
PSI_ESTIMATOR_TypeDef   psi;
// Наблюдатель Скорости PMSM (Speed Estimator)
SPD_ESTIMATOR_TypeDef   se;
// Регулятор Скорости c Ограничителем
SPD_PID_TypeDef         pid_spd;
// Измеряемые величины на силовом модуле
MCKIT_TypeDef           McKit;
// Следящий за вектором потокосцепления контур ФАПЧ
PLL_TypeDef             Pll;
// Неинверти-щий преобразователь Кларка (для подхвата по ЭДС)
PCLARKE_TypeDef         uvwEab;
// Массив указателей на выводимые в ЦАП координаты
_iq *signal[16], kf[16];

// Переменные низкоприоритетной Машины Состояний
static void (*Alpha_FSM_Task)(void);           // указатель на задачу
static unsigned long VirtualTimer     = 0;     // виртуальный таймер
static unsigned int FSM_TicCounter    = 0;     // счетчик тиков

/* Private function prototypes -----------------------------------------------*/
static void PMSM_CntrlUnit_SetBlkParam(void);
static void PMSM_CntrlUnit_CreateWires(void);
static void PMSM_CntrlUnit_Reset_IC(void);
static void State_drive_OFF(void);
static void State_drive_CAP(void);
static void State_drive_POS(void);
static void State_drive_SIX(void);
static void State_drive_RUN(void);
static void Goto_OFF_State(void);
static void MODIFY_CAP_2_RUN(void);
static void MODIFY_CAP_2_POS(void);
static void MODIFY_POS_2_SIX(void);
static void MODIFY_SIX_2_RUN(void);
static void MODIFY_RUN_2_OFF(void); // \
static void MODIFY_SIX_2_OFF(void); == MODIFY_RUN_2_OFF \
static void MODIFY_POS_2_OFF(void); == MODIFY_RUN_2_OFF \
static void MODIFY_OFF_2_CAP(void); == MODIFY_RUN_2_OFF

/*******************************************************************************
* Function Name : main
* Description :   Main program
*******************************************************************************/
#if defined STM32_F3_DISCOVERY
#pragma optimize=none  // 4 IAR: Иначе Оптимизатор кода отключает Alpha_FSM_Task
#endif
int main(void) {
    PWMDRV_InitTypeDef  pwm;
    // Определим количество циклов CPU, которые считает Таймер формируя ШИМ, (Q0)
    pwm.PeriodMax   = PWM_RESOLUTION - 1;
    // Определим полу-период ШИМ-а в циклах CPU, (Q0)
    pwm.HalfPerMax  = (PWM_RESOLUTION >> 1) - 1;
    // Определим величину бестоковой паузы в циклах CPU, (Q0)
    pwm.Deadband    = PWM_DEADBAND; // == PWM_DEADBAND / CPU_SYSCLK, uS
    pwm.CnfgReg.all =
        (SW_H_POLARITY  << 0) + // Активный уровень предрайверов верхних ключей
        (SW_L_POLARITY  << 1) + // Активный уровень предрайверов  нижних ключей
        (SW_H_IDLESTATE << 2) + // Уровень сигнала для верхних ключей в IDLE-режиме
        (SW_L_IDLESTATE << 3) + // Уровень сигнала для  нижних ключей в IDLE-режиме
        (MCU_OPEN_DRAIN << 4) + // 1, если питание у MCU и у Моста раздельное
        (SVPWM_ON       << 5);  // 0, если мала полоса ОУ (не работает на ХХ)

    MCU_Init(&pwm);

    //PMSM_CntrlUnit_CreateBlock(); // Создаем матем. блоки ЦСУ для PMSM
    PMSM_CntrlUnit_SetBlkParam();   // Устанавливаем параметры блоков
    PMSM_CntrlUnit_CreateWires();   // Определяем схему передачи аргументов
    PMSM_CntrlUnit_Reset_IC();      // Предустанавливаем Начальные Условия

    MCU_Start();

    unsigned long zVirtualTimer = 0;    // Регистр задержки для тика таймера
    unsigned int ISR_TicCounter = 0;    // Счетчик тиков таймера
    Alpha_FSM_Task = &State_drive_OFF;  // Предустановили задачу для FSM

    // Машина состояний для низкоприоритетных операций:
    // опрос копок, индикация, сетевая коммуникация и тп.
    while (1) {
        zVirtualTimer = VirtualTimer;             // запомнили  тик таймера
        while (VirtualTimer == zVirtualTimer) {}  // ждем новый тик таймера
        // Через каждые 20 mS (FSM_TIMESTEP) обслуживаем Машину Состояний
        if (++ISR_TicCounter >= (unsigned int)(FSM_TIMESTEP / TIMESTEP)) {
            ISR_TicCounter = 0; (*Alpha_FSM_Task)();
        }
    }
}

static void State_drive_OFF(void) {
    // Декрементируем счетчик времени блокировки кнопки
    if (FSM_TicCounter) { FSM_TicCounter -= 1; return; }
    // Опрос кнопки включения
    if (IS_BUTTON_PRESS == 0) return;

    // Переход к состоянию ...

    // Режим позиционирования / торможения
    State = POS;
    // Модифицируем блок-схему
    //pid_spd.gS = &Rotor.gS;
    Rotor.yPhi = &Rotor.phi;
    PMSM_CntrlUnit_Reset_IC();
    // Включаем двигатель
    EPWM_on();
    // Планируем позиционирование на 1 сек
    FSM_TicCounter = (unsigned int) (1.0 /* сек */ / FSM_TIMESTEP);
    // Меняем указатель на задачу
    Alpha_FSM_Task = &State_drive_POS;
}

static void State_drive_POS(void) {
    // Подсчет времени позиционирования
    if (FSM_TicCounter) { FSM_TicCounter -= 1; return; }

    // Переход к состоянию ...

    // Режим синхронного запуска
    State = SIX;
    // Подготовка к запуску в синхронном режиме
    Rotor.zo     = _IQ(0.0);    // Сброс запускающего интегратора
    // Меняем указатель на задачу
    Alpha_FSM_Task = &State_drive_SIX;
}

static void State_drive_SIX(void) {
    // Ожидание разгона двигателя
    if (Rotor.zo < _IQ(START_F /* Гц */ )) { return; }

    // Переход к состоянию ...

    // Режим штатного функционирования
    State = RUN;
    // Модифицируем блок-схему
    pid_spd.gS = &SetPnt.gS;
    pid_spd.zo = 0;
    Rotor.yPhi = &psi.phi;
    //Rotor.yPhi = &Pll.phi;
    // Меняем указатель на задачу
    Alpha_FSM_Task = &State_drive_RUN;
}

static void State_drive_RUN(void) {
    // Опрос кнопки включения
    if (IS_BUTTON_PRESS == 0) return;

    // Переход к состоянию ...

    // Выключаем двигатель
    EPWM_off();
    // Частотный преобразователь выключен
    State = OFF;
    // Планируем блокировку кнопки на 1 сек
    FSM_TicCounter = (unsigned int) (1.0 /* сек */ / FSM_TIMESTEP);
    // Меняем указатель на задачу
    Alpha_FSM_Task = &State_drive_OFF;
}

/*******************************************************************************
* Function Name : PMSM_CntrlUnit_SetBlkParam
* Description :   ...
*     Процедура установки параметров математических блоков
*     (объектов программы), которые составляют ЦСУ для СДПМ.
*     Не беспокойтесь о делениях! Подобные выражения вычислит препроцессор
*     компилятора. Тут просто константы будут присваиваться переменным.
*******************************************************************************/
static void PMSM_CntrlUnit_SetBlkParam(void) {
    // Измеряемые величины на силовом модуле *********************************
    McKit.Vdc  = _IQ(1.0);                                    // заглушка    *
    McKit.Idc  = _IQ(0.0);                                    // заглушка    *
    McKit.Em   = _IQ(1.0);                                    // заглушка    *
    McKit.Vm   =  SVPWM_ON ? _IQ(2.0 / M_SQRT3) : _IQ(1.0);   // заглушка    *
    McKit.uIs0 = _IQ(CS_U0);                                  //             *
    McKit.vIs0 = _IQ(CS_V0);                                  //             *
    // Подчиненный PI-регулятор q-проекции тока статора СДПМ (ось момента) ***
    pid_iq.Kp  = _IQ(KP_C);                                   // тюнинг KK_C *
    pid_iq.Ki  = _IQ(KP_C / TI_C * CURRENT_LOOP_TIMESTEP);    // не меняется *
    // Подчиненный PI-регулятор d-проекции тока статора СДПМ (ось поля) ******
    pid_id.Kp  = _IQ(KP_C);                                   // тюнинг KK_C *
    pid_id.Ki  = _IQ(KP_C / TI_C * CURRENT_LOOP_TIMESTEP);    // не меняется *
    // Регулятор Скорости c Ограничителем ************************************
    pid_spd.Kp = _IQ(KP_S);                                   // мастер      *
    pid_spd.Ki =  TI_S == 0 ? _IQ(0.0) :                      // мастер      *
                 _IQ(KP_S / TI_S * SPEED_LOOP_TIMESTEP);      //             *
    pid_spd.Lx = _IQ(LES);                                    // тюнинг LES  *
    // Глобальные уставки Цифровой системы управления для СДПМ (PMSM / BLAC) *
    SetPnt.gU  = _IQ(1.0);                                    //             *
    SetPnt.gS  =  SAFE_S ? _IQ(SAFE_S) :                      //             *
                 _IQ(IS * RS * (1.0 / KE) / BASE_OMEGA);      //             *
    SetPnt.gI  = _IQ(SAFE_I);                                 //             *
    SetPnt.g0  = _IQ(0.0);                                    // 0 для PMSM  *
    SetPnt.GND = _IQ(0.0);                                    // GND = 0     *
    SetPnt.SVPWM_Enable = SVPWM_ON ? ENABLE : DISABLE;        //             *
    // Параметры запускающего интегратора ************************************
    Rotor.gS   = _IQ(2.0 * M_PI * START_F / BASE_OMEGA);      // START_F     *
  //Rotor.gS   = _IQ(IS * RS * (1.0 / KE) / BASE_OMEGA);      //             *
    // Следящий за вектором потокосцепления контур ФАПЧ **********************
    //Pll.Kp = _IQ((1 / (3*TIMESTEP)) / BASE_OMEGA / (1<<4)); // Kp = 2 / Ti *
    //Pll.Ki = _IQ((1 / (3*TIMESTEP)) / BASE_OMEGA / (1<<4)   // Ti = 2..8dT *
    //                / (3*TIMESTEP)  * TIMESTEP);            // Ti <= Ls/Rs *
    // Обратное масштабирование (Kp и Ki << 4) см. в PLL_MACRO ***************
}

/*******************************************************************************
* Function Name : PMSM_CntrlUnit_CreateWires
* Description :   ...
*     Процедура выполняет построение цифровой системы управления для
*     СДПМ (PMSM) из вычислительных модулей - экземпляров объектов.
*     Суть построения ЦСУ - определение схемы передачи аргументов.
*     Выход - это атрибут модуля (переменная принадлежит объекту).
*     Вход - не является атрибутом модуля (объект имеет указатели
*     для подключения к аргументам).
*******************************************************************************/
static void PMSM_CntrlUnit_CreateWires(void) {
    // Указатель на угловое положение вала
    Rotor.yPhi   = &psi.phi;        // Подключили к углу с арктангенса
    // Регуляторы Тока (РТ) для проекций тока статора
    pid_iq.gI    = &pid_spd.uS;     // Подключили к регулятору задающий сигнал
    pid_iq.yI    = &ab2dq.q;        // Подключили ОС - q-проекцию тока статора
    pid_iq.gC    = &se.yS;          // Подключили сигнал канала комб. упр-ния
    pid_id.gI    = &SetPnt.g0;      // Подключили к регулятору задающий сигнал
    pid_id.yI    = &ab2dq.d;        // Подключили ОС - d-проекцию тока статора
    pid_id.gC    = &SetPnt.GND;     // Подключили сигнал канала комб. упр-ния
    // Инверсный преобразователь Парка
    dq2ab.d      = &pid_id.uV;      // Подключили выходной сигнал РТ для d-оси
    dq2ab.q      = &pid_iq.uV;      // Подключили выходной сигнал РТ для q-оси
    dq2ab.Sine   = &Rotor.Sine;     // Подключили ротатор к опорной синусоиде
    dq2ab.Cosine = &Rotor.Cosine;   // Подключили ротатор к опорной косинусоиде
    // Преобразователь числа фаз
    ab2uvw.a     = &dq2ab.a;        // Подключили Alpha-фазу к преобразователю
    ab2uvw.b     = &dq2ab.b;        // Подключили  Beta-фазу к преобразователю
    // Преобразователь числа фаз
    uvw2ab.u     = &McKit.uIs;      // Подключили датчик тока фазы  "U" (UVW)
    uvw2ab.v     = &McKit.vIs;      // Подключили датчик тока фазы  "V" (UVW)
    // Ротатор (Преобразователь Парка)
    ab2dq.a      = &uvw2ab.a;       // Подключили ток Alpha-фазы
    ab2dq.b      = &uvw2ab.b;       // Подключили ток  Beta-фазы
    ab2dq.Sine   = &Rotor.Sine;     // Подключили ротатор к опорной   синусоиде
    ab2dq.Cosine = &Rotor.Cosine;   // Подключили ротатор к опорной косинусоиде
    // Наблюдатель потокосцеплений
    psi.Vm       = &McKit.Vm;       // Подключили вход к датчику Напряжения
    psi.Va       = &dq2ab.a;        // Подключили напряжение Alpha-фазы
    psi.Vb       = &dq2ab.b;        // Подключили напряжение  Beta-фазы
    psi.Ia       = &uvw2ab.a;       // Подключили ток        Alpha-фазы
    psi.Ib       = &uvw2ab.b;       // Подключили ток         Beta-фазы
    // Наблюдатель Скорости (Speed Estimator)
    se.Vm        = &McKit.Vm;       // Подключили вход к датчику Напряжения
    se.Vq        = &pid_iq.uV;      // Подключили вход к уставке Скважности
    se.Iq        = &ab2dq.q;        // Подключили вход к датчику Тока
    // Регулятор Скорости (РС)
    pid_spd.gS   = &SetPnt.gS;      // Подключили Уставку Скорости
    pid_spd.yS   = &se.yS;          // Подключили Наблюдатель Скорости
    pid_spd.gI   = &SetPnt.gI;      // Подключили Уставку тока (момента)
    // Следящий за вектором потокосцепления контур ФАПЧ
    //Pll.a      = &psi.a;          // Подключили потокосцепление Alpha-фазы
    //Pll.b      = &psi.b;          // Подключили потокосцепление  Beta-фазы
    //Pll.yS     = &se.yS;          // Подключили канал КУ к наблюд. скорости

    // Шаблон преобразования целого числа A [0..3600] к полной шкале ЦАПа (12bit)
    // _iq dd = (((long) A) << (GL_Q - 11)) - (_IQ(3600.0 / 4096 / 2) << 1);
    // dd = _IQmpy(dd, _IQ(4096.0 / 3600)) + _IQ(1.0); // результат GL_Q: [0.0, 2.0]

    // Токи Alpha- и Beta-фазы (контроль смещений нуля)
    signal[0] = &McKit.uIs;     kf[0] = _IQ(80.0);
    signal[1] = &McKit.vIs;     kf[1] = _IQ(80.0);
    signal[1] = &SetPnt.GND;    kf[1] = _IQ(1.00);
    // Угол и потокосцепление Alpha-фазы (контроль KE)
    signal[2] = &Rotor.phi;     kf[2] = _IQ(1 / M_PI);
    signal[3] = &psi.a;         kf[3] = _IQ(1 / 1.5);
    // Угол и скорость наблюдателя экв. ДПТ (настр. РС)
    signal[4] = &Rotor.phi;     kf[4] = _IQ(1 / M_PI);
    signal[5] = &se.yS;         kf[5] = _IQ(1);
    // Ток Alpha-фазы и моментный ток (настр. РТ по шуму)
    signal[6] = &uvw2ab.a;      kf[6] = _IQ(4.0);
    signal[7] = &ab2dq.q;       kf[7] = _IQ(4.0);
    signal[7] = &pid_iq.uV;     kf[7] = _IQ(1.0);
    // Составляющие тока по моментной оси и магнитной
    signal[8] = &ab2dq.q;       kf[8] = _IQ(4.0);
    signal[9] = &ab2dq.d;       kf[9] = _IQ(4.0);
    signal[9] = &se.yS;         kf[9] = _IQ(1);
    // Модуль потокосцепления и ошибка ФАПЧ
    signal[10] = &Pll.d;        kf[10] = _IQ(1 / 1.5);
    signal[11] = &Pll.q;        kf[11] = _IQ(1 / 1.5);
    // Угол и скорость контура ФАПЧ
    signal[12] = &Rotor.phi;    kf[12] = _IQ(1 / M_PI);
    signal[13] = &Pll.Spd;      kf[13] = _IQ(1);
    // Угол и Модуль потокосцепления (усредненный - Psi)
    signal[14] = &Rotor.phi;    kf[14] = _IQ(1 / M_PI);
    signal[15] = &Pll.d;        kf[15] = _IQ(1 / 1.5);
    SetPnt.iC  = 0;
    SetPnt.iR  = 6;
}

static inline void MODIFY_RUN_2_OFF(void) {
    /* http://model.exponenta.ru/k2/Jigrein/JS/fwlink.htm#A2F2                */\
    /* Наблюдатель потокосцеплений                                            */\
    psi.Vm       = &McKit.Em;      /* Подключили вход к Em = 1.0              */\
    psi.Va       = &uvwEab.a;      /* Подключили противо-ЭДС Alpha-фазы       */\
    psi.Vb       = &uvwEab.b;      /* Подключили противо-ЭДС  Beta-фазы       */\
    psi.Ia       = &SetPnt.GND;    /* Подключили вход к нулю                  */\
    psi.Ib       = &SetPnt.GND;    /* Подключили вход к нулю                  */\
    /* Наблюдатель Скорости (Speed Estimator)                                 */\
    se.Vm        = &McKit.Em;      /* Подключили вход к Em = 1.0              */\
    se.Vq        = &Pll.Spd;       /* Подключили вход к фильтру скорости      */\
    se.Iq        = &SetPnt.GND;    /* Подключили вход к нулю                  */\
    /* Следящий за вектором потокосцепления контур ФАПЧ                       */\
    Pll.yS       = &se.yS09;       /* Подключили канал КУ к наблюд. скорости  */\
    /* Предустановим начальные условия                                        */\
    Pll.Spd      =
    Pll.Psi      =
    se.zo        = 0;
}

static inline void MODIFY_CAP_2_RUN(void) {
    /* Наблюдатель потокосцеплений                                            */\
    psi.Vm       = &McKit.Vm;      /* Подключили вход к датчику Напряжения    */\
    psi.Va       = &dq2ab.a;       /* Подключили напряжение Alpha-фазы        */\
    psi.Vb       = &dq2ab.b;       /* Подключили напряжение  Beta-фазы        */\
    psi.Ia       = &uvw2ab.a;      /* Подключили ток        Alpha-фазы        */\
    psi.Ib       = &uvw2ab.b;      /* Подключили ток         Beta-фазы        */\
    /* Наблюдатель Скорости (Speed Estimator)                                 */\
    se.Vm        = &McKit.Vm;      /* Подключили вход к датчику Напряжения    */\
    se.Vq        = &pid_iq.uV;     /* Подключили вход к уставке Скважности    */\
    se.Iq        = &ab2dq.q;       /* Подключили вход к датчику Тока          */\
    /* Следящий за вектором потокосцепления контур ФАПЧ                       */\
    Pll.yS       = &se.yS;         /* Подключили канал КУ к наблюд. скорости  */\
    /*                                                                        */\
    pid_spd.gS   = &SetPnt.gS;     /* Подключили Уставку Скорости             */\
    Rotor.yPhi   = &psi.phi;       /* Подключили к углу с арктангенса         */\
    Rotor.yPhi   = &Pll.phi;       /* Подключили к углу контура ФАПЧ          */\
    /* Предустановим начальные условия                                        */\
    pid_iq.zo    =
    pid_id.zo    = 0;
}

static inline void MODIFY_CAP_2_POS(void) {
    /* MODIFY_CAP_2_RUN();                                                    */
    PMSM_CntrlUnit_Reset_IC();     /* Сброс всех начальных условий            */
    Rotor.yPhi   = &Rotor.phi;     /* Закольцевали указатель (для запуска)    */
    /* pid_spd.gS   = &Rotor.gS;      Хорошая идея №1, но не универсальная    */
    /* SetPnt.g0    = _IQ(SAFE_I);    Хорошая идея №2, но не универсальная    */
}

static inline void MODIFY_POS_2_SIX(void) {
    Rotor.zo     = _IQ(0.0);       /* Сброс запускающего интегратора          */
    Pll.CapCntr  = 0;              /* Сброс счётчика времени захвата          */
}

static inline void MODIFY_SIX_2_RUN(void) {
    pid_spd.gS   = &SetPnt.gS;     /* Подключили Уставку Скорости             */\
    pid_spd.zo   = ab2dq.q;        /* Мягко скоммутируем (без п.п.)           */\
    Rotor.yPhi   = &psi.phi;       /* Подключили к углу с арктангенса         */\
    Rotor.yPhi   = &Pll.phi;       /* Подключили к углу контура ФАПЧ          */\
}

/*******************************************************************************
* Function Name : PMSM_CntrlUnit_Reset_IC
* Description :   ...
*     Процедура сброса / предустановки Начальных Условий для элементов
*     с эффектом памяти (регистров задержки и интеграторов).
*     Если программа ЦСУ по блок-схеме собрана корректно, то для сброса
*     к начальному состоянию достаточно предустановить начальные условия
*     на регистрах задержки (на интеграторах). Остальные координаты будут
*     корректно рассчитаны от источников Уставок до Силового Моста.
*******************************************************************************/
static void PMSM_CntrlUnit_Reset_IC(void) {
    // 1. Сбросим начальные условия на блоках с эффектом памяти (Reset IC)
    psi.zoA = psi.zoB = 0;
    //Pll.phi = Pll.zo = Pll.Spd = Pll.Psi = 0;
    Rotor.phi = 0;
    pid_iq.zo = pid_id.zo = 0;
    se.zo = pid_spd.zo = 0;
    // 2. Поскольку алгоритм системы управления предполагает
    // разные периоды дискретизации для модулей, и закрепленные
    // за ними вычисления могут быть выполнены не при первом
    // вызове MainISR. Предустановим их выходы.
    se.yS = pid_spd.uS = 0; // pid_iq.uV = pid_id.uV =
}

/*******************************************************************************
* Function Name : DMA1_Channel1_IRQHandler
* Description :   Самая главная процедура программы (обработка прерывания)
*     Лучше всего сконфигурировать функционирование Таймера, АЦП,
*     и контроллера ПДП, так чтобы прерывание, которое формирует последний,
*     не приходилось прореживать программно. Т.е. чтобы период вызова
*     соответствующей функции совпадал с требуемы шагом дискретизации
*     цифровой системы управления (TIMESTEP).
*     Поглощающий прерывания / события счетчик реверсов Таймера лучше
*     не использовать. Рекомендуется увеличить буфер АЦП для результатов
*     оцифровки сигналов. И корректно настроить котроллер ПДП.
*     В этом случае результаты оцифровки можно усреднять.
*******************************************************************************/
#ifdef LAUNCHXL_F28027F
__interrupt void MainISR(void)
#elif defined STM32_F3_DISCOVERY
ADCRESULT_TypeDef       adc;
#pragma optimize=speed
void DMA1_Channel1_IRQHandler(void)
#else
void MainISR(void)
#endif
{
    // ------------------------------------------------------------------------------
    //  Инкрементируем виртуальный таймер и ограничиваем счет 15-ю битами
    // ------------------------------------------------------------------------------
    VirtualTimer++;                     // VirtualTimer &= 0x00007FFF;

    if (State == OFF || State == CAP) {
        //ADC_OFFSET_MACRO(McKit);      // Усреднение   смещения нуля
        ADC_MEAS_MACRO(McKit);          // Для контроля смещения нуля
        unsigned short i = VirtualTimer & 1 ? SetPnt.iC : SetPnt.iC + 1;
        _iq signal1 = _IQmpy(*signal[i], kf[i]) + _IQ(1.0);
        DAC_MACRO(_IQtoIQ15(signal1));
        CLEAR_IT_PENDING_BIT();
        return;
    }

    GPIO_TOGGLE_MACRO(); // Формируем синхросигнал (инвертируем бит порта)
    // ------------------------------------------------------------------------------
    //  Увеличим угол для синхронного вращения (Start mode)
    // ------------------------------------------------------------------------------
    if (State == SIX) {
        // START_F - частота тока, при которой привод устойчиво запускается
        // START_F * 1.2 - максимальная частота тока при синхронном запуске
        // START_F / 5.0 - приращение   частоты тока при синхронном запуске
        if (Rotor.zo  < _IQ((START_F * 1.20) /* Гц */ )) {
            Rotor.zo += _IQ((START_F / 5.00) /* Гц / сек */ * TIMESTEP);
        }
        Rotor.phi += _IQmpy(SetPnt.gS > 0 ? Rotor.zo : -Rotor.zo,
                                     _IQ(2 * M_PI * TIMESTEP));
        if (Rotor.phi > +_IQ(M_PI)) Rotor.phi -= _IQ(2 * M_PI);
        if (Rotor.phi < -_IQ(M_PI)) Rotor.phi += _IQ(2 * M_PI);
    }
    // ------------------------------------------------------------------------------
    //  Для текущего Углового положения ротора вычислим sin / cos
    // ------------------------------------------------------------------------------
    Rotor.phi    = *Rotor.yPhi;
    _iq help_var = 0; // для макросов _IQsin и _IQcos
    Rotor.Sine   = _IQsin(Rotor.phi, help_var);
    Rotor.Cosine = _IQcos(Rotor.phi, help_var);
    // +2.2 uS

    // ------------------------------------------------------------------------------
    //  Приведём результаты измерений фазных токов к отн. единицам [-1.0, +1.0]
    // ------------------------------------------------------------------------------
    ADC_MEAS_MACRO(McKit);          // +1.5 uS
    // ------------------------------------------------------------------------------
    //  Вычислим максимальную амплитуду фазного напряжения (pu), [0.0, 1.0]
    // ------------------------------------------------------------------------------
    McKit.Vm = SetPnt.SVPWM_Enable
        ? _IQmpy(McKit.Vdc, _IQ(2.0 / M_SQRT3)) : McKit.Vdc;
    // ------------------------------------------------------------------------------
    //  Преобразуем токи 3-x фазной системы в токи эквивалентной 2-x фазной
    // ------------------------------------------------------------------------------
    uvw2ab.a = *uvw2ab.u;
    uvw2ab.b = _IQmpy((*uvw2ab.u + _IQmpy2(*uvw2ab.v)), _IQ(1.0 / M_SQRT3));
    // +1.2 uS
    // ------------------------------------------------------------------------------
    //  Перейдем от 2-x фазной системы токов к постоянному току (к d- q-проекциям)
    // ------------------------------------------------------------------------------
    ab2dq.d = _IQmpy(*ab2dq.a, *ab2dq.Cosine) + _IQmpy(*ab2dq.b, *ab2dq.Sine);
    ab2dq.q = _IQmpy(*ab2dq.b, *ab2dq.Cosine) - _IQmpy(*ab2dq.a, *ab2dq.Sine);
    // +1.4 uS

    // ------------------------------------------------------------------------------
    //  Вызываем макросы ПИ-регулятора контура скорости, и обновления пределов Уставки тока
    // ------------------------------------------------------------------------------
    // TODO: pid_spd.Lx = _IQmpy(*pid_spd.gI, _IQ(LES));
    SPD_PID_MACRO(pid_spd);
    // Если Регулятор Скорости не используется, обнулим Начальное Условие на 1/s
    //if (is_pHystReversMode) pid_spd.zo = 0;

    // ------------------------------------------------------------------------------
    //  Вызываем макросы ПИ-регуляторов контура тока
    // ------------------------------------------------------------------------------
    DQ_PID_MACRO(pid_iq);   // Моментная q-ось
    DQ_PID_MACRO(pid_id);   // Намагничивающая d-ось
    // +4.7 uS

    // ------------------------------------------------------------------------------
    //  Преобразуем управляющие сигналы Регуляторов тока в 2-x фазную систему напряжений
    // ------------------------------------------------------------------------------
    dq2ab.a = _IQmpy(*dq2ab.d, *dq2ab.Cosine) - _IQmpy(*dq2ab.q, *dq2ab.Sine);
    dq2ab.b = _IQmpy(*dq2ab.q, *dq2ab.Cosine) + _IQmpy(*dq2ab.d, *dq2ab.Sine);
    // +1.4 uS
    // ------------------------------------------------------------------------------
    //  Преобразуем 2-x фазную систему напряжений в 3-x фазную (для питания электромотора)
    // ------------------------------------------------------------------------------
    _iq temp_v1 = _IQdiv2(*ab2uvw.a);
    _iq temp_v2 = _IQmpy(*ab2uvw.b, _IQ(M_SQRT3 / 2.0));
    ab2uvw.u = -(*ab2uvw.a);
    ab2uvw.v = temp_v1 - temp_v2;
    ab2uvw.w = temp_v1 + temp_v2;
    // +0.9 uS

    // ------------------------------------------------------------------------------
    //  Преобразуем 3-x фазную синусоидальную последовательность в ... и будет SVPWM
    // ------------------------------------------------------------------------------
    if (SetPnt.SVPWM_Enable) { COMBINED_SVM_MACRO(ab2uvw); }
    // SVM_MACRO: +1.9 .. +2.4 uS

    // ------------------------------------------------------------------------------
    //  Ограничиваем сигналы (чтоб не было перегрузки при преобразовании к Q0)
    // ------------------------------------------------------------------------------
    if (ab2uvw.u >= _IQ(+1.0)) ab2uvw.u = _IQ(+1.0) - 1; // ..00011111..
    if (ab2uvw.u <  _IQ(-1.0)) ab2uvw.u = _IQ(-1.0);     // ..11100000..
    if (ab2uvw.v >= _IQ(+1.0)) ab2uvw.v = _IQ(+1.0) - 1; // ..00011111..
    if (ab2uvw.v <  _IQ(-1.0)) ab2uvw.v = _IQ(-1.0);     // ..11100000..
    if (ab2uvw.w >= _IQ(+1.0)) ab2uvw.w = _IQ(+1.0) - 1; // ..00011111..
    if (ab2uvw.w <  _IQ(-1.0)) ab2uvw.w = _IQ(-1.0);     // ..11100000..
    // +1.5 uS | SVM: +1.6 .. +1.82 uS | PWM: +1.68 .. +1.86 uS
    // ------------------------------------------------------------------------------
    //  Обновляем Регистры Сравнения Таймеров или каналов одного Таймера
    // ------------------------------------------------------------------------------
    // Вариант 1 (32*32): _IQmpy(_IQdiv2(ab2uvw.u + _IQ(1.0)), PWM_RESOLUTION);
    // Вариант 2 (16*16): _Q15toBASE(_IQtoIQ15(ab2uvw.u), PWM_RESOLUTION));
    PWM_MACRO(ab2uvw);    // +2.0 uS // аргументы от _IQ(-1.0) до _IQ(+1.0) - 1

    // ------------------------------------------------------------------------------
    //  Наблюдатель Скорости (Speed Estimator)
    // ------------------------------------------------------------------------------
    SPD_ESTIMATOR_MACRO(se);                    // +0.6 uS
    // ------------------------------------------------------------------------------
    //  Вычисляем Alpha и Beta проекции вектора потокосцепления
    // ------------------------------------------------------------------------------
    PSI_ESTIMATOR_MACRO(psi);                   // +1.6 uS
    // ------------------------------------------------------------------------------
    //  Вычисляем угол вектора потокосцепления [-M_PI, +M_PI]
    // ------------------------------------------------------------------------------
    psi.phi =_IQatan2(psi.b, psi.a);            // +3.3 uS STM32_F3_DISCOVERY
                                                // +6.4 uS LAUNCHXL_F28027F
    // ------------------------------------------------------------------------------
    //  Вычисляем угол следящим за вектором потокосцепления контуром ФАПЧ
    // ------------------------------------------------------------------------------
    //PLL_MACRO(Pll);                           // +2.3 .. 2.4 uS

    #if defined STM32_F3_DISCOVERY
    // ------------------------------------------------------------------------------
    //  Используем ЦАП для контроля координат ЦСУ осциллографом
    // ------------------------------------------------------------------------------
    unsigned short i = VirtualTimer & 1 ? SetPnt.iR : SetPnt.iR + 1;
    _iq signal1 = _IQmpy(*signal[i], kf[i]) + _IQ(1.0);
    DAC_MACRO(_IQtoIQ15(signal1));
    #endif

    GPIO_TOGGLE_MACRO(); // Формируем синхросигнал (инвертируем бит порта)
    CLEAR_IT_PENDING_BIT();

    // STM32_F3_DISCOVERY.  Без оптимизации кода время исполнения функции:
    // PWM: 27.5 .. 28 uS | SVM: + 1.4 .. 1.9 uS
    // С оптимизацией по скорости: PWM: 12.4 uS

    // LAUNCHXL_F28027F.    Без оптимизации кода время исполнения функции:
    // PWM: 19 .. 20 uS | SVM: +1 uS
    // С оптимизацией по скорости: PWM: 18.8 .. 19.2 uS
}

#ifdef USE_FULL_ASSERT
/*******************************************************************************
* Function Name :       assert_failed
* Description :         Отчеты об имени исходного файла и о номере строки,
*                       где в режиме отладки произошла assert_param ошибка.
* Input : - file :      Указатель на имя исходного файла
*         - line :      Номер строки кода с ошибкой assert_param
* Output :              None
* Return :              None
*******************************************************************************/
void assert_failed(u8 * file, u32 line) {
    // Пользователь может определить собственное сообщение об имени файла
    // и номере строки.
    // Пример:
    // printf("Ошибка величины параметра: file %s on line %d\r\n", file, line);
    while (1) {}
}
#endif

/************************ END OF FILE *****************************************/

Листинг 6. Файл mcu-s.h

/******************************************************************************
* File Name :   mcu-s.h
* Author :      Клиначев Николай Васильевич
* Version :     1.0.0
* Date :        20150707
* Target 1 :    ARM Cortex-M4 (STM32F303xC), FPU:Off
*               MCU KIT: STM32F3DISCOVERY
* Target 2 :    С2000: TMS320F2802x, FPU:none
*               MCU KIT: LAUNCHXL-F28027F + BOOSTXL-DRV8301
* Description : База данных плат микроконтроллеров (MCU KITs)
*     В базе определены ШИМ-драйверы силового трехфазного моста
*     (для питания секций статора двигателя переменного тока).
*     Для определения драйвера необходимо раскомментировать определение
*     одного каталожного идентификатора платы микроконтроллера в списке.
*     На первом этапе допустимо оставить закомментированными все определения.
*     Код содержит шаблон ШИМ-драйвера и будет успешно скомпилирован
*     без подключения библиотек производителя микроконтроллера.
*******************************************************************************/
/* Защита от рекурсивных подключений библиотеки-------------------------------*/
#ifndef __MCU_S_H
#define __MCU_S_H

#define STM32_F3_DISCOVERY      // ARM Cortex-M4 - STM32F303xC / 72 МГц
//#define LAUNCHXL_F28027F        // TI C2000 - TMS320F28027F / 60 МГц

// Структура для конфигурации ШИМ-драйвера силового моста **********************
typedef struct {
    unsigned int PeriodMax;  // Период      реверса Таймера в циклах CPU, (Q0)
    unsigned int HalfPerMax; // Полу-период реверса Таймера в циклах CPU, (Q0)
    unsigned int Deadband;   // Величина бестоковой паузы   в циклах CPU, (Q0)
    union {
        unsigned int all;
        struct {
            unsigned int SW_H_Polarity:  1; // Активный уровень предрайверов верхних ключей
            unsigned int SW_L_Polarity:  1; // Активный уровень предрайверов  нижних ключей
            unsigned int SW_H_IdleState: 1; // Уровень сигнала для верхних ключей в IDLE-режиме
            unsigned int SW_L_IdleState: 1; // Уровень сигнала для  нижних ключей в IDLE-режиме
            unsigned int MCU_Open_DRAIN: 1; // 1, если питание у MCU и у Моста раздельное
            unsigned int SVPWM_on:       1; // 0, если мала полоса ОУ (не работает на ХХ)
            unsigned int rsvd:          10; // 15:6 Зарезервированы
        } bit;
    } CnfgReg;               // Регистр конфигурации ШИМ-драйвера силового моста
} PWMDRV_InitTypeDef;


#if defined STM32_F3_DISCOVERY  // Публичные определения ШИМ-драйвера MCU
/******************************************************************************
* Target :      ARM Cortex-M4 (STM32F303xC), FPU:Off
* MCU KIT :     STM32 F3 Discovery
* DAC :         PA.05
* Sinxro :      PB.11
* Description : ...
*******************************************************************************/
#include "stm32f30x.h"  // Подключаем библиотеки производителя процессора
#define CPU_SYSCLK      72e6    // SystemCoreClock
#define ISR_PRESCALER   2       // Количество усредняемых выборок
/* Область памяти, для обращения к которой можно использовать:
* либо имя массива в котором хранятся 32-х разрядные слова (4 шт.),
* либо имя массива в котором хранятся 16-и разрядные полуслова (8 шт.).
*
* adc.Dual[0] == { adc.Single[0], adc.Single[1] }
* adc.Dual[1] == { adc.Single[2], adc.Single[3] }
* adc.Dual[2] == { adc.Single[4], adc.Single[5] }
* adc.Dual[3] == { adc.Single[6], adc.Single[7] }
*
* adc.Single[0] ==  АЦП1, 1-ая выборка без знака (ток фазы статора uIs)
* adc.Single[1] ==  АЦП2, 1-ая выборка без знака (ток фазы статора vIs)
* adc.Single[2] ==  АЦП1, 2-ая выборка без знака (ток фазы статора uIs)
* adc.Single[3] ==  АЦП2, 2-ая выборка без знака (ток фазы статора vIs)
* adc.Single[4]  -  значение, полученное при самокалибровке АЦП1
* adc.Single[5]  -  значение, полученное при самокалибровке АЦП2
* adc.Single[6]  -  среднее по двум выборкам АЦП1 со знаком, тип данных: _iq11
* adc.Single[7]  -  среднее по двум выборкам АЦП2 со знаком, тип данных: _iq11
*/
typedef union {
    uint32_t   Dual[ISR_PRESCALER + 2];       // массив 32-х разрядных слов
    uint16_t Single[ISR_PRESCALER * 2 + 4];   // массив 16-х разрядных полуслов
} ADCRESULT_TypeDef;                          // сырые данные АЦП1 и АЦП2
extern ADCRESULT_TypeDef        adc;          // Массив оцифрованных значений
// Объявление прототипа функции обработки прерывания ***************************
void DMA1_Channel1_IRQHandler(void);
// Функция инициализации микроконтроллера **************************************
void MCU_Init(PWMDRV_InitTypeDef* pwm);
// Функция запуска микроконтроллера (разрешения прерываний) ********************
void MCU_Start(void);
// Макрос опроса кнопки пользователя на плате микроконтроллера *****************
#define IS_BUTTON_PRESS         ( GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0)     )
// Макрос переключения GPIO-выхода для контроля / или синхронизации осцилл-а ***
#define GPIO_TOGGLE_MACRO()     { GPIOB->ODR ^= GPIO_Pin_11;                   }
// Макрос записи данных в ЦАП **************************************************
#define DAC_MACRO(data)         { DAC_SetChannel2Data(DAC_Align_12b_L, data);  }
// Макрос  включения предрайвера силового моста ********************************
#define EPWM_on()               { TIM_CtrlPWMOutputs(TIM1, ENABLE);            }
// Макрос выключения предрайвера силового моста ********************************
#define EPWM_off()              { TIM_CtrlPWMOutputs(TIM1, DISABLE);           }
// Макрос очистки бита подтверждения обслуживания прерывания *******************
#define CLEAR_IT_PENDING_BIT()  { DMA_ClearITPendingBit(DMA1_IT_TC1);          }
// Макрос обновления Регистров Сравнения таймер[а/ов] **************************
#define PWM_MACRO(ab2uvw)       {                                              \
    TIM_SetCompare1(TIM1, _Q15toBASE(_IQtoIQ15(ab2uvw.u), PWM_RESOLUTION));    \
    TIM_SetCompare2(TIM1, _Q15toBASE(_IQtoIQ15(ab2uvw.v), PWM_RESOLUTION));    \
    TIM_SetCompare3(TIM1, _Q15toBASE(_IQtoIQ15(ab2uvw.w), PWM_RESOLUTION));    \
}
// Макрос измерения смещения нуля датчиков тока стоек силового моста ***********
#define ADC_OFFSET_MACRO(McKit) {                                              \
    /* Сложим пары униполярных значений (для усреднения по каналам)          */\
    adc.Dual[3] = adc.Dual[0] + adc.Dual[1];        /* тип результата: _iq13 */\
    /* Оцифрованные, униполярные, 13-ти битные значения преобразуем          */\
    /* к относительным величинам в диапазоне от _IQ(0.00) до _IQ(+1.0).      */\
    /* Усредним сигналы апериодическим звеном 1-ого порядка с большой        */\
    /* постоянной времени (T = 1/1.10 сек). Ожидаемый результат для          */\
    /* токов близок к _IQ(0.5000). Фиксируем его в mckits.h                  */\
    McKit.uIs0 += _IQmpy(((long) adc.Single[6] << (GL_Q - 13))                 \
                         - McKit.uIs0, _IQ(1.10 * TIMESTEP));                  \
    McKit.vIs0 += _IQmpy(((long) adc.Single[7] << (GL_Q - 13))                 \
                         - McKit.vIs0, _IQ(1.10 * TIMESTEP));                  \
}
// Макрос измерения тока стоек силового моста и напряжения dc-шины *************
#define ADC_MEAS_MACRO(McKit)   {                                              \
    /* Сложим пары униполярных значений (для усреднения по каналам)          */\
    adc.Dual[3] = adc.Dual[0] + adc.Dual[1];        /* тип результата: _iq13 */\
    /* Оцифрованные, униполярные, 13-ти битные значения преобразуем          */\
    /* к относительным величинам: I == [_IQ(-1.0), _IQ(+1.0)]                */\
    /*                            U == [_IQ(0.00), _IQ(+1.0)]                */\
    McKit.uIs = _IQmpy2((((long) adc.Single[6]) << (GL_Q - 13)) - McKit.uIs0); \
    McKit.vIs = _IQmpy2((((long) adc.Single[7]) << (GL_Q - 13)) - McKit.vIs0); \
/* McKit.Vdc = (((long) AdcResult.ADCRESULT3) << (GL_Q - 13)) - _IQ(CS_DC0); */\
}

#elif defined LAUNCHXL_F28027F  // Публичные определения ШИМ-драйвера MCU
/******************************************************************************
* Target :      С2000: TMS320F2802x
* MCU KIT :     LAUNCHXL-F28027F
* DAC :         none
* Sinxro :      J1.5 (GPIO-34)
* Description : ...
*******************************************************************************/
#include "DSP28x_Project.h" // Подключаем библиотеки производителя процессора
// Необходимо допилить ШИМ-драйвер семейства процессоров С2000 фирмы ТИ для ...
//#define SW_H_POLARITY   1       // Активный уровень предрайверов верхних ключей
//#define SW_L_POLARITY   1       // Активный уровень предрайверов  нижних ключей
//#define SW_H_IDLESTATE  0       // Уровень сигнала для верхних ключей в IDLE-режиме
//#define SW_L_IDLESTATE  0       // Уровень сигнала для  нижних ключей в IDLE-режиме
//#define MCU_OPEN_DRAIN  0       // 1, если питание у MCU и у Моста раздельное
// ...
typedef enum { DISABLE = 0, ENABLE = !DISABLE } FunctionalState;
typedef enum { RESET = 0, SET = !RESET } FlagStatus;

#if (CPU_FRQ_60MHZ)
#define CPU_SYSCLK      60e6    // SystemCoreClock
#else
#error "PMSM: Why not defined CPU_FRQ_60MHZ for LAUNCHXL-F28027F ?"
#endif
#define ISR_PRESCALER   1       // Количество усредняемых выборок
// Укажем компилятору, что функция MainISR должна запускаться из ОЗУ ***********
#pragma CODE_SECTION(MainISR, "ramfuncs");
// Объявление прототипа функции обработки прерывания ***************************
void MainISR(void);
// Функция инициализации микроконтроллера **************************************
void MCU_Init(PWMDRV_InitTypeDef* pwm);
// Функция запуска микроконтроллера (разрешения прерываний) ********************
void MCU_Start(void);
// Макрос опроса кнопки пользователя на плате микроконтроллера *****************
#define IS_BUTTON_PRESS         ( GpioDataRegs.GPADAT.bit.GPIO12               )
// Макрос переключения GPIO-выхода для контроля / или синхронизации осцилл-а ***
#define GPIO_TOGGLE_MACRO()     { GpioDataRegs.GPBTOGGLE.bit.GPIO34 = 1;       }
// Макрос записи данных в ЦАП **************************************************
#define DAC_MACRO(data)         {                                              }
// Макрос очистки бита подтверждения обслуживания прерывания *******************
#define CLEAR_IT_PENDING_BIT()  {                                              \
    /* Снимаем блокировку прерываний таймера, подтверждая обслуживание       */\
    EPwm1Regs.ETCLR.bit.INT = 1;                                               \
    /* Снимаем блокировку для 3ей группы прерываний PIE                      */\
    PieCtrlRegs.PIEACK.all = PIEACK_GROUP3;                                    \
}
// Макрос  включения предрайвера силового моста ********************************
#define EPWM_on()               {                                              \
    EALLOW;                                                                    \
    EPwm1Regs.TZCLR.bit.OST = 1;                                               \
    EPwm2Regs.TZCLR.bit.OST = 1;                                               \
    EPwm3Regs.TZCLR.bit.OST = 1;                                               \
    EDIS;                                                                      \
}
// Макрос выключения предрайвера силового моста ********************************
#define EPWM_off()              {                                              \
    EALLOW;                                                                    \
    EPwm1Regs.TZFRC.bit.OST = 1;                                               \
    EPwm2Regs.TZFRC.bit.OST = 1;                                               \
    EPwm3Regs.TZFRC.bit.OST = 1;                                               \
    EDIS;                                                                      \
}
// Макрос обновления Регистров Сравнения таймер[а/ов] **************************
#define PWM_MACRO(ab2uvw)             {                                        \
    EPwm1Regs.CMPA.half.CMPA = ((PWM_RESOLUTION >> 1) - 1)                     \
            + _IQmpy(ab2uvw.u, ((PWM_RESOLUTION >> 1) - 1));                   \
    EPwm2Regs.CMPA.half.CMPA = ((PWM_RESOLUTION >> 1) - 1)                     \
            + _IQmpy(ab2uvw.v, ((PWM_RESOLUTION >> 1) - 1));                   \
    EPwm3Regs.CMPA.half.CMPA = ((PWM_RESOLUTION >> 1) - 1)                     \
            + _IQmpy(ab2uvw.w, ((PWM_RESOLUTION >> 1) - 1));                   \
}
// Макрос измерения смещения нуля датчиков тока стоек силового моста ***********
#define ADC_OFFSET_MACRO(McKit) {                                              \
    McKit.uIs0 += _IQmpy(((long) AdcResult.ADCRESULT0 << (GL_Q - 12))          \
                         - McKit.uIs0, _IQ(0.2 * TIMESTEP));                   \
    McKit.vIs0 += _IQmpy(((long) AdcResult.ADCRESULT1 << (GL_Q - 12))          \
                         - McKit.vIs0, _IQ(0.2 * TIMESTEP));                   \
}
// Макрос измерения тока стоек силового моста и напряжения dc-шины *************
#define ADC_MEAS_MACRO(McKit)   {                                              \
    McKit.uIs =                                                                \
        -_IQmpy2((((long) AdcResult.ADCRESULT0) << (GL_Q - 12)) - McKit.uIs0); \
    McKit.vIs =                                                                \
        -_IQmpy2((((long) AdcResult.ADCRESULT1) << (GL_Q - 12)) - McKit.vIs0); \
    McKit.Vdc =  (((long) AdcResult.ADCRESULT3) << (GL_Q - 12)) - _IQ(CS_DC0); \
}

#else                           // Шаблон определений ШИМ-драйвера MCU

typedef enum { DISABLE = 0, ENABLE = !DISABLE } FunctionalState;
typedef enum { RESET = 0, SET = !RESET } FlagStatus;
#define CPU_SYSCLK      72e6    // SystemCoreClock
#define ISR_PRESCALER   1       // Количество усредняемых выборок
// Объявление прототипа функции обработки прерывания ***************************
void MainISR(void);
// Функция инициализации микроконтроллера **************************************
#define MCU_Init(pwm)           {                                              }
// Функция запуска микроконтроллера (разрешения прерываний) ********************
#define MCU_Start()             {                                              }
// Макрос опроса кнопки пользователя на плате микроконтроллера *****************
#define IS_BUTTON_PRESS         ( 0 == 0                                       )
// Макрос переключения GPIO-выхода для контроля / или синхронизации осцилл-а ***
#define GPIO_TOGGLE_MACRO()     {                                              }
// Макрос записи данных в ЦАП **************************************************
#define DAC_MACRO(data)         {                                              }
// Макрос очистки бита подтверждения обслуживания прерывания *******************
#define CLEAR_IT_PENDING_BIT()  {                                              }
// Макрос  включения предрайвера силового моста ********************************
#define EPWM_on()               {                                              }
// Макрос выключения предрайвера силового моста ********************************
#define EPWM_off()              {                                              }
// Макрос обновления Регистров Сравнения таймер[а/ов] **************************
#define PWM_MACRO(ab2uvw)       {                                              }
// Макрос измерения смещения нуля датчиков тока стоек силового моста ***********
#define ADC_OFFSET_MACRO(McKit) {                                              }
// Макрос измерения тока стоек силового моста и напряжения dc-шины *************
#define ADC_MEAS_MACRO(McKit)   {                                              }

#endif

#endif /* __MCU_S_H */

Листинг 7. Файл mcu-s.с

/******************************************************************************
* File Name :   mcu-s.c
* Author :      Клиначев Николай Васильевич
* Version :     1.0.0
* Date :        20150707
* Description : ...
*       ...
*******************************************************************************/
#include "mcu-s.h"

#if defined STM32_F3_DISCOVERY

/*******************************************************************************
* Function Name : RCC_Configuration
* Description :   Reset and Clock Control.
*******************************************************************************/
static void RCC_Configuration(void) {
#ifdef NONE_SYSTEM_STM32F30X_C
    // Сбрасываем состояние системного генератора к начальному
    RCC_DeInit();       // RCC system reset (for debug purpose)
    // Включаем внешний высокочастотный кварцевый генератор
    RCC_HSEConfig(RCC_HSE_ON);

    // Ожидаем выхода генератора на рабочий режим
    if (RCC_WaitForHSEStartUp() == !SUCCESS) {
        while (1) {} // неисправен тактовый генератор
    }
    // Активируем буфер упреждающей выборки для флеш-памяти
    FLASH_PrefetchBufferCmd(ENABLE);
    // Определим задержку для Флеш-памяти
    FLASH_SetLatency(FLASH_Latency_2);

    // Настроим тактовый генератор процессора,
    //      см. док: DM00043574.pdf, стр. 127, Figure 13
    // Конфигурируем делитель для: ядра, памяти, AHB-шины, ПДП
    RCC_HCLKConfig(RCC_SYSCLK_Div1);        // HCLK = SYSCLK
    // Конфигурируем делитель для высокоскоростной периферии
    RCC_PCLK2Config(RCC_HCLK_Div1);         // PCLK2 = HCLK
    // Конфигурируем делитель для низкоскоростной периферии
    RCC_PCLK1Config(RCC_HCLK_Div2);         // PCLK1 = HCLK/2

    // Установим коэффициент умножения частоты кварца
    // для системного генератора PLLCLK = 8MHz * 9 = 72 MHz
    RCC_PLLConfig(RCC_PLLSource_PREDIV1, RCC_PLLMul_9);
    // Включаем контур ФАПЧ (PLL) системного генератора
    RCC_PLLCmd(ENABLE);
    // Ожидаем завершения переходного процесса в контуре ФАПЧ
    while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET) {}
    // Выбираем контур ФАПЧ в качестве системного генератора
    RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); // SYSCLK = PLLCLK
    // Ожидаем выхода на рабочий режим всех делителей
    while (RCC_GetSYSCLKSource() != 0x08) {}
#endif

    // Настроим делитель частоты системного генератора для АЦП12
    RCC_ADCCLKConfig(RCC_ADC12PLLCLK_Div1); // ADCCLK = PLLCLK

    // Включаем тактирование устройств на системной AHB-шине с высокой
    // пропускной способностью (Advanced High-performance Bus)
    RCC_AHBPeriphClockCmd(
        RCC_AHBPeriph_GPIOA |   // порт A
        RCC_AHBPeriph_GPIOB |   // порт B
        RCC_AHBPeriph_DMA1  |   // контроллер ПДП
        RCC_AHBPeriph_ADC12,    // АЦП1 и АЦП2
        ENABLE);
    // Включаем тактирование устройств на APB2-шине
    // высокоскоростной периферии: TIM1
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
    // Включаем тактирование устройств на APB1-шине
    // низкоскоростной периферии: DAC1
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);
}

/*******************************************************************************
* Function Name : NVIC_Configuration
* Description : Определяем таблицу векторов прерываний и настраиваем приоритеты
*******************************************************************************/
static void NVIC_Configuration(void) {
    NVIC_InitTypeDef NVIC_InitStructure;

#ifdef NONE_SYSTEM_STM32F30X_C
#ifdef VECT_TAB_SRAM
    // Разместим таблицу векторов прерываний в ОЗУ: 0x20000000
    NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0);
#else
    // Разместим таблицу векторов прерываний в ПЗУ: 0x08000000
    NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);
#endif
#endif

    // Определим, как прерывания будут сгруппированы по приоритетам
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
    // Определим вектор прерывания для контроллера ПДП (DMA1_IT_TC1)
    NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

/*******************************************************************************
* Function Name : GPIO_Configuration
* Description : Configure the TIM1 Pins.
*******************************************************************************/
static void GPIO_Configuration(void) {
    GPIO_InitTypeDef GPIO_InitStructure;
    // Запросим параметры по умолчанию для терминалов
    GPIO_StructInit(&GPIO_InitStructure);
    // Подправим одно из значений по умолчанию
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    // Порт A: Терминал 5 - аналоговый выход для DAC1_OUT2
    GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_5;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    // Порт A: Терминал 3 - аналоговый вход для PA3 === ADC1_IN4
    // Порт A: Терминал 4 - аналоговый вход для PA4 === ADC2_IN1
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_4;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    // Порт A: Терминал 1 - цифровой вход подключенный к кнопке
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    // Порт B: Терминал 11 для контроля осциллографом при отладке
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    // Порт A: Терминалы 8, 9, 10 конфигурируем для вывода PWM
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    // Порт B: Терминалы 13, 14, 15 конфигурируем для вывода PWM
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    // Подключаем терминалы к альтернативным источникам сигнала (TIM1_PWM)
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource8, GPIO_AF_6);  // TIM1_CH1
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_6);  // TIM1_CH2
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_6); // TIM1_CH3
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource13, GPIO_AF_6); // TIM1_CH1N
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource14, GPIO_AF_6); // TIM1_CH2N
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource15, GPIO_AF_4); // TIM1_CH3N
}

/*******************************************************************************
* Function Name : TIM_Configuration
* Description :   Конфигурируем тоймер TIM1
*     Генерируем 3 пары комплементарных ШИМ-сигналов с бестоковой паузой (для
*     контроля состояния ключей стоек полупроводникового 3-х фазного моста).
*     При изменении частоты ШИМ-драйвера меняется максимальное значение,
*     до которого таймер считает тактирующие импульсы. Уставки для регистров
*     сравнения должны учитывать разрешение ШИ-модулятора (должны быть меньше).
*     Для их масштабирования предложен макрос _Q15toBASE, которому, в качестве
*     аргументов, передается выборка и параметр PWM_RESOLUTION.
*******************************************************************************/
static void TIM_Configuration(PWMDRV_InitTypeDef* pwm) {
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    TIM_OCInitTypeDef       TIM_OCInitStructure;
    TIM_BDTRInitTypeDef     TIM_BDTRInitStructure;

    // Настроим Таймер ШИМ-драйвера
    //
    // 1. Настроим двоичный делитель тактирующей частоты
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    // 2. Настроим поглощающий счетчик счетных импульсов
    TIM_TimeBaseStructure.TIM_Prescaler = 0;
    // 3. Активируем режим реверсивного счёта (inc/dec)
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_CenterAligned1;
    // 4. Установим максимальное число для счёта
    TIM_TimeBaseStructure.TIM_Period = pwm->PeriodMax;
    // 5. Настроим поглощающий прерывания счетчик (реверсов)
    TIM_TimeBaseStructure.TIM_RepetitionCounter = (ISR_PRESCALER) - 1;
    // и как ... сконфигурируем!
    TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);

    // Настроим выходы каналов Захвата / Сравнения таймера
    //
    // 1. Установим режим сравнения CCR >= CNT или CCR <= CNT
    //    Изменить, если шунты установлены в стойках и ток = 0 !!!
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
    // 2. Активируем выход OCx Регистра Сравнения (верхний ключ)
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    // 3. Активируем выход OCNx Регистра Сравнения (нижний ключ)
    TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;
    // 4. Установим полярность сигнала OCx для верхнего ключа
    TIM_OCInitStructure.TIM_OCPolarity = pwm->CnfgReg.bit.SW_H_Polarity
        ? TIM_OCPolarity_High : TIM_OCPolarity_Low;
    // 5. Установим полярность сигнала OCNx для нижнего ключа
    TIM_OCInitStructure.TIM_OCNPolarity = pwm->CnfgReg.bit.SW_L_Polarity
        ? TIM_OCNPolarity_High : TIM_OCNPolarity_Low;
    // 6. Определим состояние выходов OCx  для IDLE режима
    TIM_OCInitStructure.TIM_OCIdleState = pwm->CnfgReg.bit.SW_H_IdleState
        ? TIM_OCIdleState_Set : TIM_OCIdleState_Reset;
    // 7. Определим состояние выходов OCNx для IDLE режима
    TIM_OCInitStructure.TIM_OCNIdleState = pwm->CnfgReg.bit.SW_L_IdleState
        ? TIM_OCNIdleState_Set : TIM_OCNIdleState_Reset;
    // Установим число в Регистра Сравнения = половину от ARR
    TIM_OCInitStructure.TIM_Pulse = pwm->HalfPerMax;
    // TIM1: Конфигурируем каналы: 1, 2, 3 (заполение 50%)
    TIM_OC1Init(TIM1, &TIM_OCInitStructure);
    TIM_OC2Init(TIM1, &TIM_OCInitStructure);
    TIM_OC3Init(TIM1, &TIM_OCInitStructure);

    // Настроим защиту моста (ST.com: DM00080497.pdf DM00042534.pdf)
    //
    // 1. Определим величину бестоковой паузы
    TIM_BDTRInitStructure.TIM_DeadTime = pwm->Deadband;
    // 2. [Де]Активируем Компаратор Защиты
    TIM_BDTRInitStructure.TIM_Break = TIM_Break_Disable;
    // 3. Укажем активный уровень для Компаратора Защиты
    TIM_BDTRInitStructure.TIM_BreakPolarity = TIM_BreakPolarity_High;
    // 4. Установим уровень срабатывания Компаратора Защиты
    TIM_BDTRInitStructure.TIM_LOCKLevel = TIM_LOCKLevel_1;
    // 5. Не используем однотактный режим управления мостом (Run-режим)
    TIM_BDTRInitStructure.TIM_OSSRState = TIM_OSSRState_Enable;
    // 6. Не отключаем таймер от выходов при выключении ШИМ-а (Idle-режим)
    TIM_BDTRInitStructure.TIM_OSSIState = TIM_OSSIState_Enable;
    // 7. Не используем автоматическое включение после срабатывания защиты
    TIM_BDTRInitStructure.TIM_AutomaticOutput = TIM_AutomaticOutput_Disable;
    // и как ... сконфигурируем!
    TIM_BDTRConfig(TIM1, &TIM_BDTRInitStructure);

    // TIM1: Подаем на Триггер Событий (TRGO) сигнал обновления
    TIM_SelectOutputTrigger(TIM1, TIM_TRGOSource_Update);
}

/*******************************************************************************
* Function Name : ADC_Configuration
* Description :   Измеряемые величины: iU, iV, Udc, ...
*     Два АЦП мК конфигурируем для выполнения "Регулярных" измерений.
*     Запуск АЦП1 и АЦП2 производиться синхронно, по триггеру таймера
*     TIM1_TRGO, который срабатывает при обновлении счетчика.
*     По готовности результата оба АЦП формируют запрос на обслуживание
*     контроллеру ПДП. Который пересылает результаты в ОЗУ (в массив).
*******************************************************************************/
#pragma optimize=none  // Иначе Оптимизатор кода вырежет пустые циклы
static void ADC_Configuration(void) {
    ADC_CommonInitTypeDef   ADC_CommonInitStructure;
    ADC_InitTypeDef         ADC_InitStructure;

    // АЦП1 и АЦП2: uIs, vIs, и надо Udc
    // Выполним процедуру калибровки АЦП1 и АЦП2
    //
    // Включим регулятор напряжения ???
    ADC_VoltageRegulatorCmd(ADC1, ENABLE);
    ADC_VoltageRegulatorCmd(ADC2, ENABLE);
    // Подождем 10 uS
    for (u32 i = 720; i != 0; i--); // Delay(10);
    // Выберем режим калибровки для АЦП1
    ADC_SelectCalibrationMode(ADC1, ADC_CalibrationMode_Single);
    ADC_StartCalibration(ADC1);
    // Выберем режим калибровки для АЦП2
    ADC_SelectCalibrationMode(ADC2, ADC_CalibrationMode_Single);
    ADC_StartCalibration(ADC2);
    // Ожидаем завершения калибровки АЦП1
    while (ADC_GetCalibrationStatus(ADC1) != RESET);
    while (ADC_GetCalibrationStatus(ADC2) != RESET);
    // ... какой то глюк в функции (с STM32F303RBT6)
    // for (u32 i = 720; i != 0; i--); // Delay(10);
    adc.Single[ISR_PRESCALER * 2 + 0] = ADC_GetCalibrationValue(ADC1);
    adc.Single[ISR_PRESCALER * 2 + 1] = ADC_GetCalibrationValue(ADC2);

    // АЦП1 и АЦП2: сконфигурируем для работы в паре
    //  DM00043574.pdf, стр. 358, 15.3.29 Dual ADC modes
    // Режим преобразований: Независимый или Альтернативы ...
    ADC_CommonInitStructure.ADC_Mode = ADC_Mode_RegSimul;
    // Выберем тактовый генератор высокоскоростной AHB-шины
    ADC_CommonInitStructure.ADC_Clock = ADC_Clock_SynClkModeDiv1;
    // Конфигурируем выдачу данных для контроллера ПДП
    ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_1;
    // Укажем режим выдачи данных контроллеру ПДП
    ADC_CommonInitStructure.ADC_DMAMode = ADC_DMAMode_Circular;
    // Укажем величину задержки между стадиями Выборки
    ADC_CommonInitStructure.ADC_TwoSamplingDelay = 10;
    // и как ... настроим!
    ADC_CommonInit(ADC1, &ADC_CommonInitStructure);

    // Триггеры для запуска АЦП перечислены в документе
    // DM00043574.pdf, стр. 329, Table 89.
    // См. так же параграф 20.3.25 ADC synchronization, стр. 563

    // Для АЦП Настроим:
    ADC_StructInit(&ADC_InitStructure);
    // 1. Разрешение в битах
    ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
    // 2. Выравнивание данных
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
    // 3. Триггер внешней синхронизации запуска (TIM1_TRGO)
    ADC_InitStructure.ADC_ExternalTrigConvEvent = ADC_ExternalTrigConvEvent_9;
    // 4. Запускающий АЦП фронт триггера
    ADC_InitStructure.ADC_ExternalTrigEventEdge = ADC_ExternalTrigEventEdge_RisingEdge;
    // 5. Число каналов для преобразования (перечисленных в секвенсоре)
    ADC_InitStructure.ADC_NbrOfRegChannel = 1;
    // 6. Режим преобразования: по событиям или непрерывный
    ADC_InitStructure.ADC_ContinuousConvMode = ADC_ContinuousConvMode_Disable;
    // 7. Режим автоинжекции преобразований после Регулярной группы
    ADC_InitStructure.ADC_AutoInjMode = ADC_AutoInjec_Disable;
    // 8. Режим блокирования новых результатов, если старые не считаны
    ADC_InitStructure.ADC_OverrunMode = ADC_OverrunMode_Disable;
    // и как ... настроим!
    ADC_Init(ADC1, &ADC_InitStructure);
    ADC_Init(ADC2, &ADC_InitStructure);

    // ADC1: Укажем источник сигнала, позицию в секвенсоре, и время выборки
    // PC1 === ADC1_IN7, позиция в секвенсоре, и время выборки
    // PA3 === ADC1_IN4, позиция в секвенсоре, и время выборки
    ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 1, ADC_SampleTime_7Cycles5);
    // ADC2: Укажем источник сигнала, позицию в секвенсоре, и время выборки
    // PC1 === ADC2_IN7, позиция в секвенсоре, и время выборки
    // PA4 === ADC2_IN1, позиция в секвенсоре, и время выборки
    // PB2 === ADC2_IN12, позиция в секвенсоре, и время выборки
    ADC_RegularChannelConfig(ADC2, ADC_Channel_1, 1, ADC_SampleTime_7Cycles5);

    // Конфигурируем режим запроса канала ПДП
    ADC_DMAConfig(ADC1, ADC_DMAMode_Circular);
}

/*******************************************************************************
* Function Name : DMA_Configuration
* Description :   Настройка 1-ого канала 1-ого контроллера ПДП (DMA1 Channel1)
*     Контроллер прямого доступа в память настраиваем в режим кольцевого
*     буфера для результатов преобразования АЦП. При каждом запросе от
*     ведущего АЦП, контроллер ПДП переносит в память одно 32-х разрядное
*     слово (в котором результаты преобразования двух АЦП), инкрементирует
*     адрес ОЗУ и переходит в режим ожидания следующего запроса.
*     Когда буфер заполняется, передача данных считается завершенной,
*     контроллер формирует прерывание (накапливаются 2 результата).
*******************************************************************************/
static void DMA_Configuration(void) {
    DMA_InitTypeDef DMA_InitStructure;

    // Настроим контроллер ПДП для обслуживания ADC1 и ADC2
    // Источник запроса: ADC1, DM00043574.pdf, p.273, Table 77.
    DMA_DeInit(DMA1_Channel1);
    // Укажем адрес периферийного устройства (УВВ) т.е. АЦП
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) &ADC1_2->CDR;
    // Укажем адрес массива в ОЗУ (для результатов преобразования)
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t) &adc;
    // Укажем направление передачи данных: из АЦП в ОЗУ
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
    // Укажем размер массива для результатов преобразования
    DMA_InitStructure.DMA_BufferSize = ISR_PRESCALER;
    // Укажем, что адрес УВВ (АЦП) инкрементировать не нужно
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    // Укажем, что адрес в ОЗУ необходимо инкрементировать
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    // Укажем размер данных по указанному адресу периферии
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
    // Укажем размер данных по указанному адресу в ОЗУ
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
    // Укажем режим кольцевого буфера для канала ПДП
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
    // Установим приоритет для канала ПДП
    DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
    // Укажем, что канал не используется для передачи данных из ОЗУ в ОЗУ
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    // Инициализируем канал контроллера ПДП
    DMA_Init(DMA1_Channel1, &DMA_InitStructure);
}

/*******************************************************************************
* Function Name : OPAMP_Config
* Description :  
*******************************************************************************/
static void OPAMP_Config(void)
{
    OPAMP_InitTypeDef OPAMP_InitStructure;
    // OPAMP2: +inp === PA7, -inp === PC5, out === PA6
    OPAMP_InitStructure.OPAMP_NonInvertingInput = OPAMP_NonInvertingInput_IO4;
    OPAMP_InitStructure.OPAMP_InvertingInput = OPAMP_InvertingInput_IO1;
    OPAMP_Init(OPAMP_Selection_OPAMP2, &OPAMP_InitStructure);
    // OPAMP3: +inp === PB0, -inp === PB2, out === PB1
    OPAMP_InitStructure.OPAMP_NonInvertingInput = OPAMP_NonInvertingInput_IO4;
    OPAMP_InitStructure.OPAMP_InvertingInput = OPAMP_InvertingInput_IO2;
    OPAMP_Init(OPAMP_Selection_OPAMP3, &OPAMP_InitStructure);
    // OPAMP4: +inp === PB11, -inp === PB10, out === PB12
    OPAMP_InitStructure.OPAMP_NonInvertingInput = OPAMP_NonInvertingInput_IO2;
    OPAMP_InitStructure.OPAMP_InvertingInput = OPAMP_InvertingInput_IO1;
    OPAMP_Init(OPAMP_Selection_OPAMP4, &OPAMP_InitStructure);
}

/*******************************************************************************
* Function Name : DAC_Config
* Description :   Конфигурируем 2-ой канал ЦАП-а
*******************************************************************************/
static void DAC_Configuration(void) {
    DAC_InitTypeDef     DAC_InitStructure;
    // Запросим параметры по умолчанию для ЦАП-а
    DAC_StructInit(&DAC_InitStructure);
    // DAC_DeInit();

    // Настроим 2-ой канал 1-ого ЦАП-а для отладки СУ
    //
    // Укажем триггер внешней синхронизации
    DAC_InitStructure.DAC_Trigger = DAC_Trigger_None;
    // Деактивируем встроенный генератор шума и треугольника
    DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None;
    // Определим количество разрядов для встроенного генератора
    DAC_InitStructure.DAC_LFSRUnmask_TriangleAmplitude = DAC_LFSRUnmask_Bit0;
    // Деактивируем буферизирующий усилитель на выходе ЦАП-а
    DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Disable;
    // Инициализируем 2-ой канал 1-ого ЦАП-а
    DAC_Init(DAC_Channel_2, &DAC_InitStructure);
}

void MCU_Init(PWMDRV_InitTypeDef* pwm) {
    // Настраиваем тактовый генератор процессора
    RCC_Configuration();
    // Заполняем таблицу векторов прерываний
    NVIC_Configuration();
    // Конфигурируем порты ввода / вывода
    GPIO_Configuration();
    // Конфигурируем контроллеры ПДП
    DMA_Configuration();
    // Конфигурируем ЦАП-ы
    DAC_Configuration();
    // Конфигурируем ОУ
    // OPAMP_Config();
    // Конфигурируем АЦП
    ADC_Configuration();
    // Конфигурируем Таймеры
    TIM_Configuration(pwm);
}

void MCU_Start(void) {
    //OPAMP_Cmd(OPAMP_Selection_OPAMP2, ENABLE);
    //OPAMP_Cmd(OPAMP_Selection_OPAMP3, ENABLE);
    //OPAMP_Cmd(OPAMP_Selection_OPAMP4, ENABLE);

    // Включаем АЦП1
    ADC_Cmd(ADC1, ENABLE);
    // Ожидаем флаг готовности АЦП1
    while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_RDY));
    // Дадим волшебный пендель, чтоб все поехало
    ADC_StartConversion(ADC1);
    while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_RDY));

    // Включаем АЦП2
    ADC_Cmd(ADC2, ENABLE);
    // Ожидаем флаг готовности АЦП2
    while (!ADC_GetFlagStatus(ADC2, ADC_FLAG_RDY));
    // Дадим волшебный пендель, чтоб все поехало
    ADC_StartConversion(ADC2);
    while (!ADC_GetFlagStatus(ADC2, ADC_FLAG_RDY));

    // Активируем запросы Устройств к контроллерам ПДП
    ADC_DMACmd(ADC1, ENABLE);
    // Включаем каналы контроллеров ПДП
    DMA_Cmd(DMA1_Channel1, ENABLE); // ADC1
    //DMA_Cmd(DMA2_Channel5, ENABLE); // ADC3
    //DMA_Cmd(DMA1_Channel5, ENABLE); // TIM1
    //DMA_Cmd(DMA2_Channel1, ENABLE); // TIM8
    // ...

    // Включаем прерывания Устройств
    // Сбрасываем флаг прерывания по завершению передачи
    DMA_ClearFlag(DMA1_FLAG_TC1);
    // Разрешаем прерывание по завершению передачи
    DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE);

    // Включаем 2-ой канал 1-ого ЦАП-а PA.05
    DAC_Cmd(DAC_Channel_2, ENABLE);

    // Включаем Таймеры стробирования Устройств
    // Включаем счетчик таймера ШИМ-драйвера
    TIM_Cmd(TIM1, ENABLE);
    // Не включаем главный ШИМ-выход таймера
    //TIM_CtrlPWMOutputs(TIM1, ENABLE);
    //TIM_CtrlPWMOutputs(TIM8, ENABLE);
    // ...
}

#elif defined LAUNCHXL_F28027F

#include "f2802x_common/source/f2802x_sysctrl.c"
#include "f2802x_common/source/f2802x_piectrl.c"
#include "f2802x_common/source/f2802x_pievect.c"
#include "f2802xpwm.h"      // ШИМ-драйвер
#include "f2802xileg_vdc.h" // Драйвер АЦП

void MCU_Init(PWMDRV_InitTypeDef* pPWM) {
    // ВНИМАНИЕ: Убедитесь, что вы вызвали memcpy до запуска каких-либо
    // функций из ОЗУ. InitSysCtrl вызывает функции из ОЗУ, и, без
    // вызова memcpy, процессор будет исполнять случайный код
    #ifdef _FLASH
    memcpy(&RamfuncsRunStart, &RamfuncsLoadStart, (size_t) &RamfuncsLoadSize);
    #endif

    // Step 1. Настраиваем тактовый генератор процессора
    InitSysCtrl();

    // Step 2. Конфигурируем порты ввода / вывода
    EALLOW;
    //--------------------------------------------------------------------------
    GpioCtrlRegs.GPAPUD.bit.GPIO0 = 1;    // Disable pull-up on GPIO0 (EPWM1A)
    GpioCtrlRegs.GPAPUD.bit.GPIO1 = 1;    // Disable pull-up on GPIO1 (EPWM1B)
    GpioCtrlRegs.GPAPUD.bit.GPIO2 = 1;    // Disable pull-up on GPIO2 (EPWM2A)
    GpioCtrlRegs.GPAPUD.bit.GPIO3 = 1;    // Disable pull-up on GPIO3 (EPWM2B)
    GpioCtrlRegs.GPAPUD.bit.GPIO4 = 1;    // Disable pull-up on GPIO4 (EPWM3A)
    GpioCtrlRegs.GPAPUD.bit.GPIO5 = 1;    // Disable pull-up on GPIO5 (EPWM3B)
    GpioCtrlRegs.GPAMUX1.bit.GPIO0 = 1;   // Configure GPIO0 as EPWM1A
    GpioCtrlRegs.GPAMUX1.bit.GPIO1 = 1;   // Configure GPIO1 as EPWM1B
    GpioCtrlRegs.GPAMUX1.bit.GPIO2 = 1;   // Configure GPIO2 as EPWM2A
    GpioCtrlRegs.GPAMUX1.bit.GPIO3 = 1;   // Configure GPIO3 as EPWM2B
    GpioCtrlRegs.GPAMUX1.bit.GPIO4 = 1;   // Configure GPIO4 as EPWM3A
    GpioCtrlRegs.GPAMUX1.bit.GPIO5 = 1;   // Configure GPIO5 as EPWM3B
    //--------------------------------------------------------------------------
    //  Функция GPIO-12 = Кнопка
    GpioCtrlRegs.GPAPUD.bit.GPIO12     = 1;  // Disable pullup on GPIO12
    GpioCtrlRegs.GPAMUX1.bit.GPIO12    = 0;  // 0=GPIO,  1=TZ1,  2=?SCITXDA,  3=Resv
    GpioCtrlRegs.GPADIR.bit.GPIO12     = 0;  // 0=INput, 1=OUTput
    //GpioDataRegs.GPACLEAR.bit.GPIO12 = 1;  // uncomment if --> Set Low initially
    //GpioDataRegs.GPASET.bit.GPIO12   = 1;  // uncomment if --> Set High initially
    //GpioCtrlRegs.GPAQSEL1.bit.GPIO12 = 3;  // asynch input
    //--------------------------------------------------------------------------
    //  Функция GPIO-34 = Синхросигнал для осциллографа
    GpioCtrlRegs.GPBPUD.bit.GPIO34     = 1;  // Disable pullup on GPIO34
    GpioCtrlRegs.GPBMUX1.bit.GPIO34    = 0;  // 0=GPIO,  1=Resv,  2=Resv,  3=Resv
    GpioCtrlRegs.GPBDIR.bit.GPIO34     = 1;  // 0=INput, 1=OUTput
    GpioDataRegs.GPBCLEAR.bit.GPIO34   = 1;  // uncomment if --> Set Low initially
    //GpioDataRegs.GPBSET.bit.GPIO34   = 1;  // uncomment if --> Set High initially
    //--------------------------------------------------------------------------
    //  Функция GPIO-6 = Включение предрайвера силового моста "EN_GATE"
    GpioCtrlRegs.GPAPUD.bit.GPIO6      = 1;  // Disable pullup on GPIO6
    GpioCtrlRegs.GPAMUX1.bit.GPIO6     = 0;  // 0=GPIO,  1=Resv,  2=Resv,  3=Resv
    GpioCtrlRegs.GPADIR.bit.GPIO6      = 1;  // 0=INput, 1=OUTput
    //GpioDataRegs.GPACLEAR.bit.GPIO6  = 1;  // uncomment if --> Set Low initially
    GpioDataRegs.GPASET.bit.GPIO6      = 1;  // uncomment if --> Set High initially
    //--------------------------------------------------------------------------
    //  Функция GPIO-7 = Включение режима калибровки датчиков предрайвра "DC-CAL"
    GpioCtrlRegs.GPAPUD.bit.GPIO7      = 1;  // Disable pullup on GPIO7
    GpioCtrlRegs.GPAMUX1.bit.GPIO7     = 0;  // 0=GPIO,  1=Resv,  2=Resv,  3=Resv
    GpioCtrlRegs.GPADIR.bit.GPIO7      = 1;  // 0=INput, 1=OUTput
    GpioDataRegs.GPACLEAR.bit.GPIO7    = 1;  // uncomment if --> Set Low initially
    //GpioDataRegs.GPASET.bit.GPIO7    = 1;  // uncomment if --> Set High initially
    EDIS;

    // Step 3. Заполняем Таблицу Векторов Прерываний
    DINT;   // Выключаем Глобальный рубильник прерываний (INTM-ключ)
    // Сбрасываем все регистры модуля Расширения Периферийных Прерываний (PIE)
    InitPieCtrl();
    // Деактивируем прерывания CPU и сбрасываем все флаги прерываний
    IER = 0x0000;
    IFR = 0x0000;

    // Инициализируем Таблицу Векторов Прерываний указателями
    // на функции-заглушки Стандартных Драйверов Периферии
    InitPieVectTable();

    // Определяем адреса ISR-функций в Таблице Векторов Прерываний
    EALLOW;     // "Открываем сейф защищаемых регистров"
    PieVectTable.EPWM1_INT = &MainISR;
    EDIS;       // "Закрываем сейф защищаемых регистров"

    // Step 4. Конфигурируем Пререферийные Устройства ВВ
    EALLOW;
    SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC = 0;
    EDIS;

    // Инициализируем модуль АЦП
    ADC_MACRO_INIT();
    // Инициализируем ШИМ-модуль
    EPWM_init(pPWM);    // Инициализируем ШИМ-модуль
    EPWM_off();         // Заблокируем выходы таймеров
    // Связываем прерывание EPWM1 с событием Счетного Регистра
    EPwm1Regs.ETSEL.bit.INTSEL  = ET_CTR_ZERO;
    // Включаем прерывания EPWM1INT
    EPwm1Regs.ETSEL.bit.INTEN   = 1;
    // Настроим поглощающий счетчик реверсов для Триггера Событий
    EPwm1Regs.ETPS.bit.INTPRD   = ET_1ST;
    // Снимаем блокировку прерываний
    EPwm1Regs.ETCLR.bit.INT     = 1;

    EALLOW;
    SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC = 1;
    EDIS;
}

void MCU_Start(void) {
    // Настраиваем модуль Расширения Периферийных Прерываний (PIE)
    PieCtrlRegs.PIECTRL.bit.ENPIE   = 1;  // Включаем PIE-модуль
    // Включаем EPWM1_INT  (в  3ей группе PIE)
    PieCtrlRegs.PIEIER3.bit.INTx1   = 1;
    // Включаем прерывания CPU для 3ей группы PIE
    IER |= M_INT3;
    // Включаем Глобальный рубильник прерываний (INTM-ключ)
    //   и Высоко приоритетные события отладчика реального времени:
    EINT;     // Включаем Глобальный рубильник прерываний (INTM-ключ)
    ERTM;     // и прерывания Отладчика Реального Времени (DBGM-ключ)
}

#endif

Листинг 8. Файл f2802xileg_vdc.h

/******************************************************************************
* File Name :   F2803XILEG_VDC_PM.H
* Originator :  Digital control systems Group - Texas Instruments
* Version :     1.0.0
* Date :        20150721
* Target :      С2000: TMS320F2802x
*               "LAUNCHXL-F28027F + BOOSTXL-DRV8301" kit
* Description : В этом h-файле определён макрос инициализации АЦП
*       (АЦП запускается по событию ET_CTR_PRD Счетного Регистра EPWM1).
*       АЦП конфигурируется для измерения:
*       токов Фаз Моста, фазных противо-ЭДС, тока и напряжения DC-шины.
*       Драйвер написан методом даунгрейда драйвера F2803xPWM_PM.H
*       Исходный драйвер куриц[а] из ТИ лапой писал[а]
*******************************************************************************/

#ifndef __F2802XILEG_VDC_H__
#define __F2802XILEG_VDC_H__

#include "f2802xbmsk.h"

/*------------------------------------------------------------------------------
ADC Initialization Macro Definition
------------------------------------------------------------------------------*/


#define ADC_usDELAY       10000L
#ifndef DELAY_US
#define CPU_CLOCK_SPEED   16.667L // for a 60MHz CPU clock speed (SYSCLKOUT)
#define DELAY_US(A)       DSP28x_usDelay(((((long double) A * 1000.0L) \
                          /(long double)CPU_CLOCK_SPEED) - 9.0L) / 5.0L)
#endif

extern void DSP28x_usDelay(unsigned long Count);


#define ADC_MACRO_INIT()                                               \
    /**/                                                               \
    DELAY_US(ADC_usDELAY);                                             \
    AdcRegs.ADCCTL1.all = ADC_RESET_FLAG;                              \
    asm(" NOP ");                                                      \
    asm(" NOP ");                                                      \
    /**/                                                               \
    EALLOW;                                                            \
    AdcRegs.ADCCTL1.bit.ADCBGPWD    = 1;   /* Включаем Bandgap-стабилизатор */\
    /**/                                                                      \
    DELAY_US(ADC_usDELAY);     /* Ожидаем завершение процесса включения АЦП */\
    /**/                                                                      \
    AdcRegs.ADCCTL1.bit.ADCREFSEL   = 0;   /* Работаем с внутренним Uref    */\
    AdcRegs.ADCCTL1.bit.ADCREFPWD   = 1;   /* Включаем питание для Uref     */\
    AdcRegs.ADCCTL1.bit.ADCPWDN     = 1;   /* Включаем питание для АЦП      */\
    AdcRegs.ADCCTL1.bit.ADCENABLE   = 1;   /* Активируем АЦП                */\
    /**/                                                                      \
    asm(" RPT#100 || NOP");                                                   \
    /**/                                                                      \
    AdcRegs.ADCCTL1.bit.INTPULSEPOS = 1;   /* Прерывания по завершению      */\
    AdcRegs.ADCCTL1.bit.TEMPCONV    = 0;                                      \
    /**/                                                                      \
    DELAY_US(ADC_usDELAY);                                                    \
    /**/                                                                      \
    /******* Подключение каналов к источникам сигнала *******/                  \
    /* Нумерация каналов: 0..7 ADCINA0..ADCINA7, 8..15 ADCINB0..ADCINB7      */ \
    /* Цоколевка в документах: slvu974.pdf или sldc006.pdf                   */ \
    AdcRegs.ADCSOC0CTL.bit.CHSEL  =  9; /* Канал АЦП ADCINB1: IA-FB          */ \
    AdcRegs.ADCSOC1CTL.bit.CHSEL  = 11; /* Канал АЦП ADCINB3: IB-FB          */ \
    AdcRegs.ADCSOC2CTL.bit.CHSEL  = 15; /* Канал АЦП ADCINB7: IC-FB          */ \
    AdcRegs.ADCSOC3CTL.bit.CHSEL  =  7; /* Канал АЦП ADCINA7: DC-V-FB        */ \
    AdcRegs.ADCSOC4CTL.bit.CHSEL  =  3; /* Канал АЦП ADCINA3: VA-FB          */ \
    AdcRegs.ADCSOC5CTL.bit.CHSEL  =  1; /* Канал АЦП ADCINA1: VB-FB          */ \
    AdcRegs.ADCSOC6CTL.bit.CHSEL  =  0; /* Канал АЦП ADCINA0: VC-FB          */ \
                                                                                \
    AdcRegs.ADCSOC0CTL.bit.TRIGSEL = 5; /* Модуль EPWM1A запускает АЦП       */ \
    AdcRegs.ADCSOC1CTL.bit.TRIGSEL = 5; /* Модуль EPWM1A запускает АЦП       */ \
    AdcRegs.ADCSOC2CTL.bit.TRIGSEL = 5; /* Модуль EPWM1A запускает АЦП       */ \
    AdcRegs.ADCSOC3CTL.bit.TRIGSEL = 5; /* Модуль EPWM1A запускает АЦП       */ \
    AdcRegs.ADCSOC4CTL.bit.TRIGSEL = 5; /* Модуль EPWM1A запускает АЦП       */ \
    AdcRegs.ADCSOC5CTL.bit.TRIGSEL = 5; /* Модуль EPWM1A запускает АЦП       */ \
    AdcRegs.ADCSOC6CTL.bit.TRIGSEL = 5; /* Модуль EPWM1A запускает АЦП       */ \
                                                                                \
    AdcRegs.ADCSOC0CTL.bit.ACQPS   = 6; /* 6+1 такт АЦП УВХ в режиме выборки */ \
    AdcRegs.ADCSOC1CTL.bit.ACQPS   = 6; /* 6+1 такт АЦП УВХ в режиме выборки */ \
    AdcRegs.ADCSOC2CTL.bit.ACQPS   = 6; /* 6+1 такт АЦП УВХ в режиме выборки */ \
    AdcRegs.ADCSOC3CTL.bit.ACQPS   = 6; /* 6+1 такт АЦП УВХ в режиме выборки */ \
    AdcRegs.ADCSOC4CTL.bit.ACQPS   = 6; /* 6+1 такт АЦП УВХ в режиме выборки */ \
    AdcRegs.ADCSOC5CTL.bit.ACQPS   = 6; /* 6+1 такт АЦП УВХ в режиме выборки */ \
    AdcRegs.ADCSOC6CTL.bit.ACQPS   = 6; /* 6+1 такт АЦП УВХ в режиме выборки */ \
    /**/                                                                           \
    EDIS;                                                                          \
    /**/                                                                           \
    /**/                                                                           \
    /* Конфигурируем источник(и) импульса(ов) для запуска преобразований         */\
    /* 1вый источник импульсов (SOCA) - событие CNT_zero Счетного Регистра EPWM1 */\
    EPwm1Regs.ETSEL.bit.SOCAEN      = 1;   /* Включаем SOCA                      */\
    EPwm1Regs.ETSEL.bit.SOCASEL     = ET_CTR_PRD; /* Событие для преоб. (SOCA)   */\
    EPwm1Regs.ETPS.bit.SOCAPRD      = 1;   /* Генерируем SOCA при 1ом событии    */\
    EPwm1Regs.ETCLR.bit.SOCA        = 1;   /* Очистка флага SOCA                 */


#endif // __F2802XILEG_VDC_H__

Листинг 9. Файл f2802xpwm.h

/******************************************************************************
* File Name :   F2802xPWM_PM.H
* Author :      Клиначев Николай Васильевич
* Version :     1.0.0
* Date :        20150721
* Target :      С2000: TMS320F2802x
* Description : ШИМ-драйвер для LAUNCHXL-F28027F + BOOSTXL-DRV8301
*       Драйвер написан методом даунгрейда драйвера F2803xPWM_PM.H
*       Исходный драйвер куриц[а] из ТИ лапой писал[а]
*******************************************************************************/

#ifndef __F2802X_PWM_H__
#define __F2802X_PWM_H__

#include "f2802xbmsk.h"

/*----------------------------------------------------------------------------
Initialization constant for the F280X Time-Base Control Registers for PWM Generation.
Sets up the timer to run free upon emulation suspend, count up-down mode
prescaler 1.
----------------------------------------------------------------------------*/
#define PWM_INIT_STATE      ( \
    FREE_RUN_FLAG           + \
    PRDLD_IMMEDIATE         + \
    TIMER_CNT_UPDN          + \
    HSPCLKDIV_PRESCALE_X_1  + \
    CLKDIV_PRESCALE_X_1     + \
    PHSDIR_CNT_UP           + \
    CNTLD_DISABLE           )

/*----------------------------------------------------------------------------
Initialization constant for the F280X Compare Control Register.
----------------------------------------------------------------------------*/
#define CMPCTL_INIT_STATE   ( \
    LOADAMODE_ZRO           + \
    LOADBMODE_ZRO           + \
    SHDWAMODE_SHADOW        + \
    SHDWBMODE_SHADOW        )

/*----------------------------------------------------------------------------
Initialization constant for the F280X Action Qualifier Output A Register.
----------------------------------------------------------------------------*/
#define AQCTLA_INIT_STATE ( CAD_SET + CAU_CLEAR )

/*----------------------------------------------------------------------------
Initialization constant for the F280X Dead-Band Generator registers for PWM Generation.
Sets up the dead band for PWM and sets up dead band values.
----------------------------------------------------------------------------*/
#define DBCTL_INIT_STATE    (BP_ENABLE + POLSEL_ACTIVE_HI_CMP)

#define DBCNT_INIT_STATE    100 // 100 counts = 1.66 usec (delay)
                                //    * 100 count/usec (for TBCLK = SYSCLK/1)

/*----------------------------------------------------------------------------
Initialization constant for the F280X PWM Chopper Control register for PWM Generation.
----------------------------------------------------------------------------*/
#define  PCCTL_INIT_STATE   CHPEN_DISABLE

/*----------------------------------------------------------------------------
Initialization constant for the F280X Trip Zone Select Register
----------------------------------------------------------------------------*/
//#define  TZSEL_INIT_STATE   ENABLE_TZ2_OST + ENABLE_TZ3_OST
#define  TZSEL_INIT_STATE   DISABLE_TZSEL

/*----------------------------------------------------------------------------
Initialization constant for the F280X Trip Zone Control Register
Управляющее слово для модуля блокировки выходов таймера
----------------------------------------------------------------------------*/
#define  TZCTL_INIT_STATE       ( \
    TZA_FORCE_LO + TZB_FORCE_LO + \
    DCAEVT1_HI_Z + DCAEVT2_HI_Z + \
    DCBEVT1_HI_Z + DCBEVT2_HI_Z )

/*------------------------------------------------------------------------------
PWM Init & PWM Update Macro Definitions
------------------------------------------------------------------------------*/
void EPWM_init(PWMDRV_InitTypeDef* v) {
    // Setup Sync
    EPwm1Regs.TBCTL.bit.SYNCOSEL = 0;
    EPwm2Regs.TBCTL.bit.SYNCOSEL = 0;
    EPwm3Regs.TBCTL.bit.SYNCOSEL = 0;
    /* */
    // Настроим тактирование и счетчики таймеров EPWM1-EPWM3
    EPwm1Regs.TBCTL.all = PWM_INIT_STATE;
    EPwm2Regs.TBCTL.all = PWM_INIT_STATE;
    EPwm3Regs.TBCTL.all = PWM_INIT_STATE;
    /* */
    /* Allow each timer to be sync'ed                              */
    EPwm1Regs.TBCTL.bit.PHSEN = 1;
    EPwm2Regs.TBCTL.bit.PHSEN = 1;
    EPwm3Regs.TBCTL.bit.PHSEN = 1;
    /* */
    /* Init Timer-Base Period Register for EPWM1-EPWM3             */
    EPwm1Regs.TBPRD = v->PeriodMax;
    EPwm2Regs.TBPRD = v->PeriodMax;
    EPwm3Regs.TBPRD = v->PeriodMax;
    /* */
    /* Init Timer-Base Phase Register for EPWM1-EPWM3              */
    EPwm1Regs.TBPHS.half.TBPHS = 0;
    EPwm2Regs.TBPHS.half.TBPHS = 0;
    EPwm3Regs.TBPHS.half.TBPHS = 0;
    /* */
    /* Init Compare Control Register for EPWM1-EPWM3               */
    EPwm1Regs.CMPCTL.all = CMPCTL_INIT_STATE;
    EPwm2Regs.CMPCTL.all = CMPCTL_INIT_STATE;
    EPwm3Regs.CMPCTL.all = CMPCTL_INIT_STATE;
    /* */
    /* Init Action Qualifier Output A Register for EPWM1-EPWM3     */
    EPwm1Regs.AQCTLA.all = AQCTLA_INIT_STATE;
    EPwm2Regs.AQCTLA.all = AQCTLA_INIT_STATE;
    EPwm3Regs.AQCTLA.all = AQCTLA_INIT_STATE;
    /* */
    /* Init Dead-Band Generator Control Register for EPWM1-EPWM3   */
    EPwm1Regs.DBCTL.all = DBCTL_INIT_STATE;
    EPwm2Regs.DBCTL.all = DBCTL_INIT_STATE;
    EPwm3Regs.DBCTL.all = DBCTL_INIT_STATE;
    /* */
    /* Init Dead-Band Generator for EPWM1-EPWM3*/
    EPwm1Regs.DBFED = v->Deadband;
    EPwm1Regs.DBRED = v->Deadband;
    EPwm2Regs.DBFED = v->Deadband;
    EPwm2Regs.DBRED = v->Deadband;
    EPwm3Regs.DBFED = v->Deadband;
    EPwm3Regs.DBRED = v->Deadband;
    /* */
    /* Init PWM Chopper Control Register for EPWM1-EPWM3           */
    EPwm1Regs.PCCTL.all = PCCTL_INIT_STATE;
    EPwm2Regs.PCCTL.all = PCCTL_INIT_STATE;
    EPwm3Regs.PCCTL.all = PCCTL_INIT_STATE;
    /* */
    EALLOW;           /* Enable EALLOW */
    /* */
    /* Init Trip Zone Select Register                              */
    EPwm1Regs.TZSEL.all = TZSEL_INIT_STATE;
    EPwm2Regs.TZSEL.all = TZSEL_INIT_STATE;
    EPwm3Regs.TZSEL.all = TZSEL_INIT_STATE;
    /* */
    /* Определим состояние выходов при выключении ШИМ-а            */
    EPwm1Regs.TZCTL.all = TZCTL_INIT_STATE;
    EPwm2Regs.TZCTL.all = TZCTL_INIT_STATE;
    EPwm3Regs.TZCTL.all = TZCTL_INIT_STATE;
    /* */
    EDIS;             /* Disable EALLOW */
}

/************************************************************************
 *  PWM Gen w Dead Band Compensation Macro
 *     if curA > 0   Ta = Ta + Tdt
 *     else          Ta = Ta - Tdt
 ************************************************************************/
#define PWMwDBC_MACRO(ch1,ch2,ch3,m,i,d)                                            \
    (*ePWM[ch1]).CMPA.half.CMPA = _IQmpy(m.HalfPerMax,m.MfuncC1)+ m.HalfPerMax +    \
                                  _IQmpy(_IQsat(i.As, d.Ith, -d.Ith), d.Kdtc);      \
    (*ePWM[ch2]).CMPA.half.CMPA = _IQmpy(m.HalfPerMax,m.MfuncC2)+ m.HalfPerMax +    \
                                  _IQmpy(_IQsat(i.Bs, d.Ith, -d.Ith), d.Kdtc);      \
    (*ePWM[ch3]).CMPA.half.CMPA = _IQmpy(m.HalfPerMax,m.MfuncC3)+ m.HalfPerMax +    \
                                  _IQmpy(_IQsat(i.Cs, d.Ith, -d.Ith), d.Kdtc);

#endif /* __F2802X_PWM_H__ */

Литература

  1. Клиначев Н.В. Определение углового положения ротора синхронного двигателя с возбуждением от постоянных магнитов / Н.В. Клиначев, Н.Ю. Кулёва, С.Г. Воронин // Вестник Южно-Уральского государственного университета. Серия: Энергетика, 2014. –Т. 14. –№ 2. –С. 49–54.
  2. Клиначёв Н.В. Подхват вращающейся синхронной машины частотным преобразователем без датчика углового положения ротора. // Моделирующая программа Jigrein: Теория, программа, руководство, модели. – 2006-2016 гг. – URL: http://model.exponenta.ru/k2/ Jigrein/JS/fwlink.htm#A2F2. Дата обращения: 27.05.2016.

7.07.2015, 27.06.2016