# Как начать тест и что смотреть в отладке

## Главное

В проект добавлены три уровня запуска:

- безопасное логирование без силовых ключей;
- тестовый PWM на каждый отдельный вход драйвера `UH/UL/VH/VL/WH/WL`;
- начальный алгоритм идентификации, который сейчас оценивает `Rs`, `Ls` и `Ll`.

По умолчанию физические PWM-выходы выключены на уровне сборки. Это сделано намеренно: прошивку можно залить и смотреть ADC без риска включить ключи.

## Где вход в приложение

Основной проект Keil:

```text
F:\set\workspace\setcorp\set506\AD\AD_Keil_Project\MDK-ARM\IHM08M.uvprojx
```

Основная Simulink-модель:

```text
F:\set\workspace\setcorp\set506\AD\Inu_im_1wnd_3lvl\inu_im_1wnd_3lvl.slx
```

В коде запуск находится в `main.c`:

```c
AD_Project_Init();

while (1)
{
    AD_Project_Loop();
}
```

## Безопасный первый запуск

Для проверки измерений без ключей:

```c
AD_Board_StartDataLogging();
```

Останов:

```c
AD_Board_StopParamTest();
```

Кнопка `B1 / PC13` переключает только `DATA_LOGGING`, силовые ключи она не включает.

## Как разрешить реальные PWM-импульсы

Перед этим нужно проверить питание, `BKIN`, полярности входов драйвера, dead-time и ограничения тока/напряжения.

В Keil, в `Options for Target -> C/C++ -> Define`, можно включить один общий макрос:

```text
AD_PROJECT_POWER_TEST_ENABLE=1
```

Он внутри включает:

```text
AD_PARAM_ID_ENABLE_POWER_TESTS=1
AD_INVERTER_ENABLE_OUTPUTS=1
```

Если `AD_PROJECT_POWER_TEST_ENABLE=0`, запрос силового режима будет переведён в безопасное логирование с флагом `AD_PARAM_ID_STATUS_POWER_TEST_BLOCKED`.

## PWM-тест каждого канала

Для проверки входов драйвера осциллографом:

```c
AD_Board_StartParamTestWithDuty(AD_PARAM_ID_MODE_PWM_TEST_UH, 0.08f);
AD_Board_StopParamTest();
```

Второй аргумент это duty. Если передать `0`, будет использовано значение по умолчанию `AD_PARAM_ID_DEFAULT_PWM_DUTY_LIMIT = 0.08`. Внутренний тестовый алгоритм дополнительно ограничивает duty до `0.20`.

Рекомендуемый порядок проверки:

| Режим | Код | Пин IHM08M1 |
|---|---:|---|
| `AD_PARAM_ID_MODE_PWM_TEST_UH` | 6 | `PA8 / TIM1_CH1 / UH` |
| `AD_PARAM_ID_MODE_PWM_TEST_UL` | 7 | `PA7 / TIM1_CH1N / UL` |
| `AD_PARAM_ID_MODE_PWM_TEST_VH` | 8 | `PA9 / TIM1_CH2 / VH` |
| `AD_PARAM_ID_MODE_PWM_TEST_VL` | 9 | `PB0 / TIM1_CH2N / VL` |
| `AD_PARAM_ID_MODE_PWM_TEST_WH` | 10 | `PA10 / TIM1_CH3 / WH` |
| `AD_PARAM_ID_MODE_PWM_TEST_WL` | 11 | `PB1 / TIM1_CH3N / WL` |
| `AD_PARAM_ID_MODE_PWM_TEST_ALL` | 12 | все три плеча, duty фаз `0.5` |

В режиме одного канала проект не перезапускает таймер каждые 10 мс: выбранный канал удерживается, а последующие шаги только обновляют compare.

## Режимы идентификации

| Режим | Код | Что делает сейчас |
|---|---:|---|
| `AD_PARAM_ID_MODE_IDLE` | 0 | выключено |
| `AD_PARAM_ID_MODE_STATOR_RESISTANCE` | 1 | оценивает `Rs_ohm` коротким DC-вектором |
| `AD_PARAM_ID_MODE_NO_LOAD_MAGNETIZING` | 2 | оценивает `Lm_H` вращающимся полем без нагрузки |
| `AD_PARAM_ID_MODE_LOCKED_ROTOR_LEAKAGE` | 3 | оценивает `Ll_H` и `Rr_ohm`; требует `AD_ParamID_SetLockedRotorAllowed(1)` |
| `AD_PARAM_ID_MODE_INERTIA_FRICTION` | 4 | оценивает `J` и `B` по разгону/выбегу; нужен ненулевой `speed_rpm` |
| `AD_PARAM_ID_MODE_DATA_LOGGING` | 5 | только измерения |
| `AD_PARAM_ID_MODE_AUTO_IDENTIFICATION` | 13 | последовательно делает `Rs`, `Ll`, `Rr`, `Lm`, затем `J/B` |
| `AD_PARAM_ID_MODE_ROTATION_3HZ` | 14 | 6-step вращение с настраиваемой частотой и модуляцией |

Запуск автоидентификации:

```c
AD_Board_StartParamTestWithDuty(AD_PARAM_ID_MODE_AUTO_IDENTIFICATION, 0.08f);
```

Запуск проверки вращения 3 Гц:

```c
AD_Board_StartParamTestWithDuty(AD_PARAM_ID_MODE_ROTATION_3HZ, 0.35f);
```

Через Modbus для этого режима:

```text
write 0x000D / 13 = 30     ; частота 3.00 Hz
write 0x000E / 14 = 3500   ; модуляция 0.35
```

`J` и `B` помечаются действительными только если во время разгона и выбега приходит скорость `speed_rpm`; при нулевой скорости алгоритм не записывает фиктивные механические параметры.

## Что смотреть в Keil

Добавить в Watch:

```c
g_ad_debug
```

Основные поля:

| Поле | Смысл |
|---|---|
| `g_ad_debug.adc.ia_raw`, `ib_raw`, `ic_raw` | сырые ADC-коды токов |
| `g_ad_debug.adc.vdc_raw_avg` | усреднённый ADC-код `BUSV` |
| `g_ad_debug.adc.vdc_adc_V` | отфильтрованное напряжение на входе `PA1/BUSV` |
| `g_ad_debug.measurements.vdc_V` | пересчитанное напряжение DC-звена |
| `g_ad_debug.command.test_mode` | запрошенный режим |
| `g_ad_debug.param_id_mode` | фактический режим |
| `g_ad_debug.inverter.pwm_running` | TIM1 PWM реально запущен |
| `g_ad_debug.inverter.service_pwm_running` | включён одиночный/сервисный PWM-тест |
| `g_ad_debug.inverter.service_output` | какой выход проверяется: `1..7` |
| `g_ad_debug.inverter.id_stage` | стадия алгоритма измерения |
| `g_ad_debug.motor_parameters.valid_mask` | какие параметры действительны |
| `g_ad_debug.motor_parameters.Rs_ohm` | измеренное сопротивление статора |
| `g_ad_debug.motor_parameters.Ls_H` | измеренная индуктивность статора |
| `g_ad_debug.motor_parameters.Ll_H` | оценка индуктивности рассеяния |

## Флаги состояния

| Флаг | Смысл |
|---|---|
| `AD_PARAM_ID_STATUS_ACTIVE` | режим активен |
| `AD_PARAM_ID_STATUS_POWER_TEST_BLOCKED` | силовой тест заблокирован |
| `AD_PARAM_ID_STATUS_POWER_STAGE_ARMED` | силовая часть программно разрешена |
| `AD_PARAM_ID_STATUS_FAULT_LATCHED` | авария защёлкнута |
| `AD_PARAM_ID_STATUS_TIMEOUT` | истекло время теста |
| `AD_PARAM_ID_STATUS_LOCKED_ROTOR_BLOCKED` | locked-rotor режим не разрешён |
| `AD_PARAM_ID_STATUS_SAFETY_LIMITS_UNKNOWN` | не заданы пределы тока или напряжения |
| `AD_PARAM_ID_STATUS_DATA_VALID` | есть актуальные измерения/данные |
| `AD_PARAM_ID_STATUS_COMPLETE` | алгоритм измерения завершён |
| `AD_PARAM_ID_STATUS_PARTIAL_COMPLETE` | автоидентификация завершилась частично: часть параметров действительна |
| `AD_PARAM_ID_STATUS_STEP_FAILED` | этап измерения не смог получить достаточный ток/напряжение/скорость |
| `AD_PARAM_ID_STATUS_LOCKED_ROTOR_SKIPPED` | locked-rotor этап пропущен, потому что `locked_rotor_allowed=0` |

Любой `Stop`, fault или переход в блокировку вызывает аппаратное выключение PWM через `AD_ParamID_HardwareDisable()`.
