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

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

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

Представленные в приложении листинги файлов полного программного кода векторной системы управления для синхронного двигателя с возбуждением от постоянных магнитов являются дополненной версией одноименных документов опубликованных и документированных в предыдущей статье, см. ссылку выше по тексту. Листинги данной статьи и предыдущей удобнее всего скопировать в файлы. Разместить их в двух разных директориях (в папках) и воспользоваться инструментами файлового менеджера Total Commander "Синхронизация каталогов" и "Сравнение содержимого файлов". Отличия, которые будут обнаружены, являются кодом, предназначенным для обработки сигналов резольвера, редуктосина или синусно-косинусного вращающегося трансформатора. В данном случае программа написана для ARM-процессора STM32F303xC. Имеющийся в листинге код для микроконтроллера TMS320F28027F не менялся. Поддерживает лишь бездатчиковую версию системы управления и сохранен для удобства работы с инструментом "Сравнение содержимого файлов".

Конфигурация аппаратных машин состояний

Для реализации векторной системы управления из модулей процессора собраны две аппаратные машины состояний. Первая предназначена для контроля движения координат электродвигателя. Вторая – для обработки сигналов резольвера.

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

Рис. 1. Осциллограммы функционирования аппаратных машин состояния
ARM-процессора сконфигурированных для обработки сигналов резольвера
и векторного управления синхронным двигателем

Первую машину состояний ведет таймер TIM1, чей счетчик работает в реверсивном режиме, формируя сигнал треугольной формы для ШИ-модулятора (см. рис. 1, нижняя осциллограмма). В моменты реверса счетчика таймер формирует синхросигнал (переворачивает аппаратный триггер), который запускает два аналогово-цифровых преобразователя в синхронном режиме (ADC1 – master и ADC2 – slave). Последние, выполняют по два преобразования, оцифровывая токи двух фаз и напряжение шины постоянного тока. По завершению, ведущий АЦП формирует запрос на обслуживание контроллеру ПДП (DMA1). Последний, активирует один из своих каналов (Channel1) и переносит результаты оцифровки в ОЗУ, формируя массив данных (выборок). По завершению цикла передачи контроллер инициирует прерывание (DMA1_Channel1_IRQn), исполняемое ядром процессора. В прерывании обрабатываются результаты измерений, обновляется значение для ШИ-модулятора таймера – формируется новое управляющее воздействие на двигатель (рис. 1, осциллограмма на фоне сигнала треугольной формы).

Вторую машину состояний, формально, следует разделить еще на две (A и B). Машину "A" ведет таймер TIM2, чей счетчик работает в инкрементном режиме. По переполнению, таймер переворачивает аппаратный триггер, формируя запрос на обслуживание контроллеру ПДП (DMA2). Последний, активирует один из своих каналов (Channel3), и, в режиме циклической передачи, переносит очередную выборку синусоиды из массива подготовленных данных (из ОЗУ) в регистр цифро-аналогового преобразователя (DAC1, Channel1). Выходной сигнал ЦАП-а подается на усилитель питающий обмотку возбуждения резольвера (рис. 1, осциллограмма синусоиды из 128 дискрет). Машину состояний "B" ведет таймер TIM3 с большим периодом счета. По переполнению, соответствующий триггер запускает в синхронном режиме два аналогово-цифровых преобразователя (ADC3 – master и ADC4 – slave) для оцифровки ортогональных сигналов резольвера. По завершению преобразования ведущий АЦП инициирует прерывание (ADC3_IRQn), исполняемое ядром процессора. Необходимая для обработки сигналов резольвера выборка опорной синусоиды (рис. 1, осциллограмма из восьми замороженных значений синусоиды) считывается из массива по индексному значению, хранящемуся в адресном регистре канала контроллера ПДП (DMA2, Channel3). Значение регистра считывает и переносит в предопределенную переменную (в ОЗУ) контроллер ПДП (DMA1, Channel3) по запросу, который формирует таймер TIM3 в момент переполнения (когда запускает преобразователи ADC3 и ADC4).

По завершению процессов конфигурации и запуска аппаратные машины состояний функционируют непрерывно до момента выключения питания, периодически инициируя два прерывания. Одно – для расчета нового управляющего воздействия (с частотой 2 кГц, можно варьировать от 1 до 20 кГц). Другое – для обработки сигналов резольвера (с частотой 32 кГц). Для наглядной визуализации загрузки ядра процессора при входе в прерывание микропроцессор поднимает бит порта ввода вывода общего назначения (GPIO). При выходе – опускает. В результате, используя осциллограф, можно наблюдать сигнал показанный в верхней части рис. 1. Узкие импульсы имеют длительность 3.2 мкс (обработка сигналов резольвера). Более широкие импульсы (14 мкс), следующие через больший временной интервал, соответствуют времени расчета регуляторов векторной системы.

Порядок настройки программного кода для обслуживания резольвера / редуктосина / СКВТ

...

  1. Настройка баланса мостового усилителя для питания обмотки возбуждения резольвера.
  2. Компенсация смещения нуля в цепях обработки выходных ортогональных сигналов резольвера.
  3. Обратная коррекция табличных значений выборок опорной синусоиды, смещенных для баланса мостового усилителя.
  4. Компенсация задержки сигнала в контуре резольвера (настройка звена чистого запаздывания для опорной синусоиды).
  5. Линеаризация выходной характеристики редуктосина смещением опорных ортогональных сигналов в преобразователе Парка.
  6. Увеличение быстродействия ПИ-регулятора следящего контура ФАПЧ (уменьшение постоянной времени интегрирующего канала).
  7. Уточнение предела ограничения сигнала на входе ПИ-регулятора следящего контура ФАПЧ (дописать строчку кода).
  8. Асинхронизация периода возбуждения резольвера и интервала оцифровки его сигналов.
  9. Коррекция угловой погрешности установки резольвера на валу электродвигателя (сигнал наблюдателя – эталон).

...

Порядок настройки программного кода ШИМ-драйвера

...

  1. Выбор частоты переключения стоек силового моста. Типовое значение 20 кГц (PWM_FREQUENCY).
  2. Выбор величины бестоковой паузы для коммутации ключей стоек силового моста. Типовое значение от 0.2 до 2 мкс. Указывается в циклах CPU, см. поле базы данных PWM_DEADBAND.
  3. Выбор частоты дискретизации системы управления, которая должна быть ниже частоты переключения стоек силового моста и быть кратна ей. Типовое значение 2 кГц. В программе задается косвенно – определением буфера (т.е. массива для оцифрованных значений токов секций обмотки статора), указанием его размера контроллеру ПДП (DMA1, Channel1), написанием кода фильтра скользящего среднего, и уточнением параметра TIMESTEP. Все упомянутое связывает значение ISR_PRESCALER.

...

Приложение. Листинги файлов исходного кода векторной системы управления для синхронного двигателя с возбуждением от постоянных магнитов и программы демодуляции сигналов резольвера следящим контуром ФАПЧ, реализованных на 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 RCos;       // Output: Измеренный сигнал Alpha-фазы резольвера,   [-1.0, 1.0]
    _iq RSin;       // Output: Измеренный сигнал  Beta-фазы резольвера,   [-1.0, 1.0]
    _iq RRef;       // Output: Опорный sin для контура ФАПЧ резольвера,   [-1.0, 1.0]
    int RTau;       // Output: Задержка опорного sin контура ФАПЧ, выборки, = 128 - X
}
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_xS;                                                                \
    sr_xS = *sr.gS - *sr.yS;                 /* Ошибка регулятора скорости  */\
    sr.uS = sr.zo + _IQmpy(sr_xS, sr.Kp);    /* Выход  регулятора скорости  */\
    sr.uS = sr.uS < _IQ(-1.0) ? _IQ(-1.0) :                                   \
            sr.uS > _IQ(+1.0) ? _IQ(+1.0) : sr.uS;                            \
    sr.uS = _IQmpy(sr.uS, *sr.gI);                                            \
    /* ---------------------------------------------------                  */\
    sr_xS = sr_xS < -sr.Lx ? -sr.Lx : sr_xS > sr.Lx ? sr.Lx : sr_xS;          \
    sr.zo = sr.zo + _IQmpy(sr_xS, sr.Ki);    /* Инт-тор регулятора скорости */\
    sr.zo = sr.zo < _IQ(-1.0) ? _IQ(-1.0) :                                   \
            sr.zo > _IQ(+1.0) ? _IQ(+1.0) : 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 *RRef;      // Input: Указатель на опорный sin для контура ФАПЧ резольвера
    _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 Sin0;       // Output: Смещение для линеаризации хар-ки редуктосина, [-1.0, 1.0]
    _iq Cos0;       // Output: Смещение для линеаризации хар-ки редуктосина, [-1.0, 1.0]
    _iq zo1;        // sPrivate: Выход регистра задержки интегратора (ГУНа) контура ФАПЧ
    _iq phi0;       // Output: Смещение углового положения резольвера,     [-M_PI, M_PI]
}
PLL_TypeDef;

// Макрос следящего за вектором потокосцепления контура ФАПЧ
//
#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.15) < PLL.q && PLL.q < _IQ(+0.15))/*  ограничивает 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 RESOLVER_PLL_MACRO(PLL)                                                 \
{                                                                               \
    _iq PRKSin, PRKCos;  /* Переременные для макросов _IQsin / _IQcos и Парка */\
    PLL.Sine   = _IQsin(PLL.zo1, PRKSin);       /*   Жаль, но датчик          */\
    PLL.Cosine = _IQcos(PLL.zo1, PRKSin);       /*     не установят идеально  */\
    PRKSin     =  PLL.Sine   + PLL.Sin0;        /* - погрешность редуктосина  */\
    PRKCos     =  PLL.Cosine + PLL.Cos0;        /* - погрешность редуктосина  */\
    /* ---------------------------------------------------------------------- */\
    /*PLL.q = _IQmpy(*PLL.b, PRKCos) - _IQmpy(*PLL.a, PRKSin);          q-ось */\
    PLL.q   = _IQmpy(*PLL.a, PRKCos) + _IQmpy(*PLL.b, PRKSin);       /* d-ось */\
    PLL.q   = _IQmpy(PLL.q, *PLL.RRef);         /*   умножили на Ref_sin      */\
    /* Защита от перегрузки во время захвата (не более 20 mS).                */\
    if (_IQ(-0.15) < PLL.q && PLL.q < _IQ(+0.15))/*  ограничивает V слежения! */\
    { PLL.q <<= 6; }                            /*   учли масштаб 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] + шум */\
    /* Защита интегратора от перегрузки во время захвата (не более 20 mS).    */\
    PLL.zo  = PLL.zo < _IQ(-1.4) ? _IQ(-1.4) :  /*   Предел позволяет следить */\
              PLL.zo > _IQ(+1.4) ? _IQ(+1.4) : PLL.zo;    /* до скорости 140% */\
    /* ---------------------------------------------------------------------- */\
    PLL.zo1 += _IQmpy(PLL.uV, _IQ(BASE_OMEGA * PLL_TIMESTEP));                  \
    if (PLL.zo1 > +_IQ(M_PI)) PLL.zo1 -= _IQ(2 * M_PI);                         \
    if (PLL.zo1 < -_IQ(M_PI)) PLL.zo1 += _IQ(2 * M_PI);                         \
    PLL.phi = PLL.zo1 + PLL.phi0;               /* коррекция установки датчика*/\
    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: Реализовать выбор 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)

// Коэффициент 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)       // Электромагнитная постоянная времени
// Ограниченная напряжением Udc максимальная частота токов статора, (рад/с)
#define BASE_OMEGA      ((BASE_VOLTAGE / 2.0) / (KE / (POLES / 2.0)))

#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

// Период вызова прерывания для обработки сигналов резольвера, (сек)
// Период вызова      = (( TIM_Period для TIM3 ) / CPU_SYSCLK)
#define PLL_TIMESTEP    ((141 * (128.0 / 8) - 1) / CPU_SYSCLK)
// Постоянная времени И-канала регулятора следящего контура ФАПЧ
// При настройке - уменьшать до появления шума (минимум PLL_TIMESTEP)
#define TI_PLL          (8.0 * PLL_TIMESTEP)    // 8.0 .. 1.0
// При уменьшении TI_PLL - менять масштабирование (1<<6) или пределы для PLL.q

/* 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 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;
// Следящий контур ФАПЧ, демодулирующий сигналы резольвера
PLL_TypeDef             RPll;
_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 Task_motor_off(void);
static void Task_motor_pos(void);
static void Task_motor_rot(void);
static void Task_motor_run(void);

/*******************************************************************************
* Function Name : main
* Description :   Main program
*******************************************************************************/
#if defined STM32_F3_DISCOVERY
#pragma optimize=none  // 4 main Иначе Оптимизатор кода отключает Alpha_FSM_Task
#endif
void 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 = &Task_motor_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 Task_motor_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 = &Task_motor_pos;
}

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

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

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

static void Task_motor_rot(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;
    Rotor.yPhi = &RPll.phi;
    // Меняем указатель на задачу
    Alpha_FSM_Task = &Task_motor_run;
}

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

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

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

/*******************************************************************************
* Function Name : PMSM_CntrlUnit_SetBlkParam
* Description :   ...
*     Процедура установки параметров математических блоков
*     (объектов программы), которые составляют ЦСУ для СДПМ.
*     Не беспокойтесь о делениях! Подобные выражения вычислит препроцессор
*     компилятора. Тут просто константы будут присваиваться переменным.
*******************************************************************************/
static void PMSM_CntrlUnit_SetBlkParam(void) {
    // Измеряемые величины на силовом модуле *********************************
    McKit.Vdc  = _IQ(1.0);                                    // заглушка    *
    McKit.Idc  = _IQ(0.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.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 ***************
    // Следящий контур ФАПЧ, демодулирующий сигналы резольвера ***************
    McKit.RTau =  128 - 39;  // задержка, Tau, на 39 выборок  // тюнинг RTau *
    RPll.Sin0  = _IQ(0.0000); // Вынести в базу данных McKit  // тюнинг      *
    RPll.Cos0  = _IQ(0.0000); // Вынести в базу данных McKit  // тюнинг      *
  //RPll.phi0  = _IQ(M_PI * 30.0 / 180);  // Вынести в McKit  // T, тюнинг   *
    RPll.phi0  = _IQ(M_PI * 0.00 / 180);  // Вынести в McKit  // Y, тюнинг   *
    RPll.Kp    = _IQ((2.0 / (TI_PLL)) / BASE_OMEGA / (1<<6)); // Kp = 2 / Ti *
    RPll.Ki    = _IQ((2.0 / (TI_PLL)) / BASE_OMEGA / (1<<6)   // Ti <= dT    *
                          / (TI_PLL)  * PLL_TIMESTEP);        // тюнинг Ti   *
    // Обратное масштабирование (Kp и Ki << 6) см. в RESOLVER_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;          // Подключили канал КУ к наблюд. скорости
    // Следящий контур ФАПЧ, демодулирующий сигналы резольвера
    RPll.RRef    = &McKit.RRef;     // Подключили опорный sin для контура ФАПЧ
    RPll.a       = &McKit.RCos;     // Подключили Alpha-фазу резольвера (cos)
    RPll.b       = &McKit.RSin;     // Подключили  Beta-фазу резольвера (sin)
    RPll.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;
}

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

#if defined STM32_F3_DISCOVERY

/*
(function (n) {
    function p(x) {
        var s = x.toString(16);
        return s.length === 2 ? s : "0" + s;
    }
    n = 0 | n;
    var s = "", x = +0, i = 0 | 0, M2PI = Math.PI * 2;
    for (i; i < n; i += 1) {
        x = 0 | ((0xff-32) * ((Math.sin(i / n * M2PI) + 1) / 2) + 30.5);
        s += "0x" + p(x) + ((i & 7) == 7 ? ",\n" : ", ");
    }
    var b = document.body,
    container = document.createElement('DIV');
    container.innerHTML = "<pre>" + s + "<\/pre>";
    b.insertBefore(container.firstChild, b.firstChild);
}(128));
*/

// Массив значений синуса для записи в ЦАП резольвера и для ...
// Смещение обеспечивает баланс мостового усилителя питающего ОВ
//
const uint8_t Sine8bit[128] = {
    // Двойной размах: 0xff-32, Смещение: 30.5
    0x8e, 0x93, 0x98, 0x9e, 0xa3, 0xa9, 0xae, 0xb3,
    0xb8, 0xbd, 0xc2, 0xc7, 0xcb, 0xd0, 0xd4, 0xd8,
    0xdc, 0xe0, 0xe4, 0xe7, 0xea, 0xed, 0xf0, 0xf2,
    0xf5, 0xf6, 0xf8, 0xfa, 0xfb, 0xfc, 0xfc, 0xfd,
    0xfd, 0xfd, 0xfc, 0xfc, 0xfb, 0xfa, 0xf8, 0xf6,
    0xf5, 0xf2, 0xf0, 0xed, 0xea, 0xe7, 0xe4, 0xe0,
    0xdc, 0xd8, 0xd4, 0xd0, 0xcb, 0xc7, 0xc2, 0xbd,
    0xb8, 0xb3, 0xae, 0xa9, 0xa3, 0x9e, 0x98, 0x93,
    0x8e, 0x88, 0x83, 0x7d, 0x78, 0x72, 0x6d, 0x68,
    0x63, 0x5e, 0x59, 0x54, 0x50, 0x4b, 0x47, 0x43,
    0x3f, 0x3b, 0x37, 0x34, 0x31, 0x2e, 0x2b, 0x29,
    0x26, 0x25, 0x23, 0x21, 0x20, 0x1f, 0x1f, 0x1e,
    0x1e, 0x1e, 0x1f, 0x1f, 0x20, 0x21, 0x23, 0x25,
    0x26, 0x29, 0x2b, 0x2e, 0x31, 0x34, 0x37, 0x3b,
    0x3f, 0x43, 0x47, 0x4b, 0x50, 0x54, 0x59, 0x5e,
    0x63, 0x68, 0x6d, 0x72, 0x78, 0x7d, 0x83, 0x88,
};

int SinIx = 0; // Индекс выборки в массиве Sine8bit

void ADC3_IRQHandler(void);

#pragma optimize=speed
void ADC3_IRQHandler(void) {
    GPIO_TOGGLE_MACRO(); // Формируем синхросигнал (инвертируем бит порта)
    // С погрешностью апертуры не более одной дискреты можно уточнить индекс
    // int ix = (int) DMA2_Channel3->CNDTR;             // ix = 128 .. 1, dec
    // ..., но запущен канал ПДП (DMA1_Channel3) в момент запуска ADC3 и ADC4
    int ix = SinIx;                                     // SinIx = 128 .. 1
    ix += McKit.RTau; if (ix >= 128) { ix -= 128; }     // Звено e^{-Ts}
    McKit.RRef = (long) (Sine8bit[ix] - 15);            // минус смещение
    McKit.RRef = (McKit.RRef << (GL_Q - 7)) - _IQ(1.0); // GL_Q, (-1.0, 1.0)
    McKit.RRef = _IQmpy(McKit.RRef, _IQ(256.0 / 224));  // GL_Q, [-1.0, 1.0]
    McKit.RCos = _IQmpy2((((long) ADC3->DR) << (GL_Q - 12)) - _IQ(0.5000));
    McKit.RSin = _IQmpy2((((long) ADC4->DR) << (GL_Q - 12)) - _IQ(0.5000));
    RESOLVER_PLL_MACRO(RPll);                           // 3.9 uS
    GPIO_TOGGLE_MACRO(); // Формируем синхросигнал (инвертируем бит порта)
    /* Код для отладки звена чистого запаздывания
    static unsigned int sw = 0;
    _iq s1 = _IQmpy(McKit.RCos, _IQ(1.0)) + _IQ(1.0);
    _iq s2 = _IQmpy(McKit.RRef, _IQ(1.0)) + _IQ(1.0);
    DAC_MACRO(_IQtoIQ15(sw & 1 ? s1 : s2));*/
    ADC_ClearITPendingBit(ADC3, ADC_IT_EOS);
}

#endif

#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;          // Массив оцифрованных значений
extern int                      SinIx;        // Индекс выборки в массиве Sin
extern const uint8_t        Sine8bit[128];    // Для записи в ЦАП резольвера
// Объявление прототипа функции обработки прерывания ***************************
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

// Адрес 8-разрядного регистра данных 1-ого канала 1-ого ЦАП-а
#define DAC_DHR8R1_ADDRESS      ((uint32_t)0x40007410)

/*******************************************************************************
* 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 и 34
    RCC_ADCCLKConfig(RCC_ADC12PLLCLK_Div1); // ADCCLK = PLLCLK
    RCC_ADCCLKConfig(RCC_ADC34PLLCLK_Div1); // ADCCLK = PLLCLK

    // Включаем тактирование устройств на системной AHB-шине с высокой
    // пропускной способностью (Advanced High-performance Bus)
    RCC_AHBPeriphClockCmd(
        RCC_AHBPeriph_GPIOA |   // порт A
        RCC_AHBPeriph_GPIOB |   // порт B
        RCC_AHBPeriph_GPIOD |   // порт D  сигналы sin/cos резольвера
        RCC_AHBPeriph_ADC12 |   // АЦП1 и АЦП2    для векторной СУ
        RCC_AHBPeriph_DMA1  |   // контроллер ПДП для векторной СУ
        RCC_AHBPeriph_DMA2  |   // контроллер ПДП для ЦАПа резольвера
        RCC_AHBPeriph_ADC34,    // АЦП3 и АЦП4    для      резольвера
        ENABLE);
    // Включаем тактирование устройств на APB2-шине
    // высокоскоростной периферии
    RCC_APB2PeriphClockCmd(
        RCC_APB2Periph_TIM1,    // Таймер ШИМ-драйвера
        ENABLE);
    // Включаем тактирование устройств на APB1-шине
    // низкоскоростной периферии
    RCC_APB1PeriphClockCmd(
        RCC_APB1Periph_DAC  |   // ЦАП для отладки и резольвера
        RCC_APB1Periph_TIM2 |   // Таймер для ЦАПа резольвера
        RCC_APB1Periph_TIM3,    // Таймер для АЦП резольвера
        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);
    // Определим вектор прерывания для Таймера (ADC34)
    NVIC_InitStructure.NVIC_IRQChannel = ADC3_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    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: Терминал 4 - аналоговый выход для DAC1_OUT1
    // Порт A: Терминал 5 - аналоговый выход для DAC1_OUT2
    GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_4 | GPIO_Pin_5;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    // Порт A: Терминал 3 - аналоговый вход для PA3 === ADC1_IN4  (uIs)
    // Порт B: Терминал 2 - аналоговый вход для PB2 === ADC2_IN12 (vIs)
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    // Порт D: Терминал 10 - аналоговый вход для PD10 === ADC34_IN7  (+cos)
    // Порт D: Терминал 11 - аналоговый вход для PD11 === ADC34_IN8  (-cos)
    // Порт D: Терминал 12 - аналоговый вход для PD12 === ADC34_IN9  (+sin)
    // Порт D: Терминал 13 - аналоговый вход для PD13 === ADC34_IN10 (-sin)
    GPIO_InitStructure.GPIO_Pin =
        GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12 | GPIO_Pin_13;
    GPIO_Init(GPIOD, &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);


    // Настроим Таймер для тактирования ЦАП-а резольвера
    //
    // 1. Настроим двоичный делитель тактирующей частоты
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    // 2. Настроим поглощающий счетчик счетных импульсов
    TIM_TimeBaseStructure.TIM_Prescaler = 0;
    // 3. Активируем режим счёта вверх (inc)
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    // 4. Установим максимальное число для счёта
    TIM_TimeBaseStructure.TIM_Period = 141 - 1;
    // 5. Настроим поглощающий прерывания счетчик реверсов
    TIM_TimeBaseStructure.TIM_RepetitionCounter = 0x0000;
    // и как ... сконфигурируем!
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
    // TIM2: Подаем на Триггер Событий (TRGO) сигнал обновления
    TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update);

    // Настроим Таймер для оцифровки сигналов резольвера
    //
    // 4. Установим максимальное число для счёта
    TIM_TimeBaseStructure.TIM_Period = 141 * (128 / 8) - 1;
    // и как ... сконфигурируем!
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
    // TIM3: Подаем на Триггер Событий (TRGO) сигнал обновления
    TIM_SelectOutputTrigger(TIM3, 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_12, 1, ADC_SampleTime_7Cycles5);

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

    // Конфигурируем режим запроса канала ПДП
    ADC_DMAConfig(ADC1, ADC_DMAMode_Circular);
    // Разрешаем запросы канала ПДП для АЦП1
    ADC_DMACmd(ADC1, ENABLE);


    // АЦП3 и АЦП4: +sin и -sin, +cos и -cos
    // Выполним процедуру калибровки АЦП3 и АЦП3
    //
    //
    // Включим регулятор напряжения ???
    ADC_VoltageRegulatorCmd(ADC3, ENABLE);
    ADC_VoltageRegulatorCmd(ADC4, ENABLE);
    // Подождем 10 uS
    for (u32 i = 720; i != 0; i--); // Delay(10);
    // Выберем режим калибровки для АЦП3
    //ADC_SelectCalibrationMode(ADC3, ADC_CalibrationMode_Differential);
    ADC_SelectCalibrationMode(ADC3, ADC_CalibrationMode_Single);
    ADC_StartCalibration(ADC3);
    // Выберем режим калибровки для АЦП4
    //ADC_SelectCalibrationMode(ADC4, ADC_CalibrationMode_Differential);
    ADC_SelectCalibrationMode(ADC4, ADC_CalibrationMode_Single);
    ADC_StartCalibration(ADC4);
    // Ожидаем завершения калибровки АЦП3
    while (ADC_GetCalibrationStatus(ADC3) != RESET);
    //adc.Single[4] = ADC_GetCalibrationValue(ADC3);
    // Ожидаем завершения калибровки АЦП4
    while (ADC_GetCalibrationStatus(ADC4) != RESET);
    //adc.Single[5] = ADC_GetCalibrationValue(ADC4);

    // АЦП3 и АЦП4: сконфигурируем для работы в паре
    //  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_Disabled;
    // Укажем режим выдачи данных контроллеру ПДП
    ADC_CommonInitStructure.ADC_DMAMode = ADC_DMAMode_OneShot;
    // Укажем величину задержки между стадиями Выборки
    ADC_CommonInitStructure.ADC_TwoSamplingDelay = 10;
    // и как ... настроим!
    ADC_CommonInit(ADC3, &ADC_CommonInitStructure);

    // Триггеры для запуска АЦП перечислены в документе
    // DM00043574.pdf, стр. 330, Table 91.
    // См. так же параграф 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. Триггер внешней синхронизации запуска (TIM3_TRGO)
    ADC_InitStructure.ADC_ExternalTrigConvEvent = ADC_ExternalTrigConvEvent_11;
    // 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(ADC3, &ADC_InitStructure);
    ADC_Init(ADC4, &ADC_InitStructure);

    // Порт D: Терминал 10 - аналоговый вход для PD10 === ADC34_IN7  (+cos)
    // Порт D: Терминал 11 - аналоговый вход для PD11 === ADC34_IN8  (-cos)
    // Порт D: Терминал 12 - аналоговый вход для PD12 === ADC34_IN9  (+sin)
    // Порт D: Терминал 13 - аналоговый вход для PD13 === ADC34_IN10 (-sin)
    //
    // ADC3: Укажем источник сигнала, позицию в секвенсоре, и время выборки
    ADC_RegularChannelConfig(ADC3, ADC_Channel_7,  1, ADC_SampleTime_7Cycles5);
    //ADC_RegularChannelConfig(ADC3, ADC_Channel_8,  2, ADC_SampleTime_7Cycles5);
    //ADC_SelectDifferentialMode(ADC3, ADC_Channel_7, ENABLE);
    // ADC4: Укажем источник сигнала, позицию в секвенсоре, и время выборки
    ADC_RegularChannelConfig(ADC4, ADC_Channel_9,  1, ADC_SampleTime_7Cycles5);
    //ADC_RegularChannelConfig(ADC4, ADC_Channel_10, 2, ADC_SampleTime_7Cycles5);
    //ADC_SelectDifferentialMode(ADC4, ADC_Channel_9, ENABLE);

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

    // Разрешим поднимать флаг прерывания по завершению всех
    // преобразований в регулярной группе заданной в секвенсоре
    //ADC_ClearFlag(ADC3, ADC_FLAG_EOS);
    //ADC_ITConfig(ADC3, ADC_IT_EOS, ENABLE);
}

/*******************************************************************************
* 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; // копим 2 результата
    // Укажем, что адрес УВВ (АЦП) инкрементировать не нужно
    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);
    // Сбрасываем флаг прерывания по завершению передачи
    DMA_ClearFlag(DMA1_FLAG_TC1);
    // Разрешаем прерывание по завершению передачи
    DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE);
    // Включаем канал контроллера ПДП
    DMA_Cmd(DMA1_Channel1, ENABLE);

    // Настроим контроллер ПДП для записи в ЦАП резольвера
    // Источник запроса: DAC_CH1, DM00043574.pdf, p.276, Table 79.
    DMA_DeInit(DMA2_Channel3);
    DMA_InitStructure.DMA_PeripheralBaseAddr = DAC_DHR8R1_ADDRESS;
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t) &Sine8bit;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
    DMA_InitStructure.DMA_BufferSize = 128;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(DMA2_Channel3, &DMA_InitStructure);

    // Настроим контроллер ПДП для считывания индекса выборки оп. синусоиды
    // Источник запроса: TIM3_UP, DM00043574.pdf, p.273, Table 77.
    DMA_DeInit(DMA1_Channel3);
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) &DMA2_Channel3->CNDTR;
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t) &SinIx;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
    DMA_InitStructure.DMA_BufferSize = 1;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
    DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(DMA1_Channel3, &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_Enable;
    // Инициализируем 2-ой канал 1-ого ЦАП-а
    DAC_Init(DAC_Channel_2, &DAC_InitStructure);

    // Настроим 1-ый канал 1-ого ЦАП-а для обслуживания резольвера
    //
    // Укажем триггер Таймера TIM2 в качестве источника синхронизации
    DAC_InitStructure.DAC_Trigger = DAC_Trigger_T2_TRGO;
    DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable;
    // Инициализируем 1-ый канал 1-ого ЦАП-а
    DAC_Init(DAC_Channel_1, &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) {
    // Включаем все, что обслуживает Резольвер
    //
    // Активируем запросы Устройств к контроллерам ПДП
    DAC_DMACmd(DAC_Channel_1, ENABLE);
    TIM_DMACmd(TIM3, TIM_DMA_Update, ENABLE);
    // Включаем контроллеры ПДП
    DMA_Cmd(DMA2_Channel3, ENABLE); // ЦАП Ch1
    DMA_Cmd(DMA1_Channel3, ENABLE); // ПДП Index
    // Включаем 1-ый канал 1-ого ЦАП-а PA.04
    DAC_Cmd(DAC_Channel_1, ENABLE);
    // Включаем Таймер стробирования ЦАП-а
    TIM_Cmd(TIM2, ENABLE);
    // Включаем Таймер стробирования измерений
    TIM_Cmd(TIM3, ENABLE);
    // Включаем прерывания ADC3
    ADC_ClearFlag(ADC3, ADC_FLAG_EOS);
    ADC_ITConfig(ADC3, ADC_IT_EOS, ENABLE);

    // Включаем 2-ой канал 1-ого ЦАП-а PA.05
    DAC_Cmd(DAC_Channel_2, ENABLE);
    // Включаем счетчик таймера ШИМ-драйвера
    TIM_Cmd(TIM1, 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__ */

7.07.2015