23 KiB
Modbus RTU регистры
Порт
Modbus RTU slave запущен на ST-LINK VCP:
| Параметр | Значение |
|---|---|
| UART | USART2 |
| TX | PA2 / USART2_TX |
| RX | PA3 / USART2_RX |
| Скорость | 512000 |
| Формат | 8N1 |
| Slave address | 1 |
Приём USART2 сделан через USART2_IRQHandler() и HAL_UARTEx_ReceiveToIdle_IT(). Готовый кадр фиксируется в HAL_UARTEx_RxEventCallback() по событию IDLE на линии UART, после этого основной цикл проверяет CRC и отправляет ответ.
Настройки задаются в Core/Inc/ad_project_config.h:
AD_MODBUS_ENABLE
AD_MODBUS_SLAVE_ADDRESS
AD_MODBUS_UART_BAUDRATE
Поддержанные функции:
| Функция | Назначение |
|---|---|
0x03 |
чтение holding registers |
0x06 |
запись одного holding register |
0x10 |
запись нескольких holding registers |
Структура holding-регистров в прошивке
Текущие значения holding-регистров зеркалируются в структуру:
MB_REGS->holding.H000_device_id
MB_REGS->holding.H035_input_volt_0p1_v
MB_REGS->holding.H048_param_valid_mask_lo
Для доступа по номеру регистра можно использовать сырой массив:
MB_REGS->holding_raw[35]
Та же карта копируется в debug-view:
g_ad_debug.modbus_regs.holding.H035_input_volt_0p1_v
Значения в структуре уже в формате Modbus-регистров uint16_t, то есть с теми же масштабами, что указаны в таблицах ниже.
Если карту нужно обновить вручную из другого модуля, вызови AD_Modbus_RefreshRegisters().
Быстрая проверка связи
После прошивки сначала проверь чтение первых двух holding-регистров:
Slave ID: 1
Function: 03 Read Holding Registers
Address: 0
Quantity: 2
Ожидаемый ответ: 0xAD01, 0x0008
Если программа на ПК работает в формате 4xxxx, используй адрес 40001 вместо 0. В прошивке поддержаны оба варианта: 0x0000 / 0 и 40001 читают один и тот же регистр device id.
Если ответа нет, проверь:
| Что смотреть | Норма |
|---|---|
| COM-порт ST-LINK VCP | выбран правильный порт платы |
| Скорость | 512000 |
| Формат | 8N1, без flow control |
| Slave ID | 1 |
| Функция | 03, не 04 |
g_ad_debug.modbus.initialized |
1 |
g_ad_debug.modbus.status_flags |
есть бит AD_MODBUS_STATUS_UART_RX_ACTIVE |
g_ad_debug.modbus.rx_frames |
растёт при правильных запросах |
g_ad_debug.modbus.crc_errors |
не должен расти при верной скорости/CRC |
g_ad_debug.modbus.uart_errors |
не должен расти; если растёт, смотри скорость, порт, шум, overrun |
Расшифровка g_ad_debug.modbus.status_flags:
| Бит | Маска | Флаг | Значение |
|---|---|---|---|
| 0 | 0x00000001 |
AD_MODBUS_STATUS_INITIALIZED |
Modbus инициализирован |
| 1 | 0x00000002 |
AD_MODBUS_STATUS_FRAME_READY |
принят кадр UART, основной цикл ещё не обработал его |
| 2 | 0x00000004 |
AD_MODBUS_STATUS_LAST_FRAME_OK |
последний обработанный кадр прошёл CRC и был принят |
| 3 | 0x00000008 |
AD_MODBUS_STATUS_LAST_FRAME_ERROR |
в последнем кадре была ошибка CRC/UART/overflow |
| 4 | 0x00000010 |
AD_MODBUS_STATUS_UART_RX_ACTIVE |
приём UART активен, HAL_UARTEx_ReceiveToIdle_IT() запущен |
| 5 | 0x00000020 |
AD_MODBUS_STATUS_RX_OVERFLOW |
переполнение RX или новый кадр пришёл до обработки предыдущего |
Нормальное состояние после старта без запросов: есть INITIALIZED и UART_RX_ACTIVE. После корректного запроса дополнительно появляется LAST_FRAME_OK, а счётчик g_ad_debug.modbus.rx_frames растёт.
Управление
| Адрес hex / dec | Поле структуры | Доступ | Масштаб | Назначение |
|---|---|---|---|---|
0x0000 / 0 |
H000_device_id |
R | 1 | device id 0xAD01 |
0x0001 / 1 |
H001_protocol_version |
R | 1 | версия карты регистров |
0x0002 / 2 |
H002_control_enable |
R/W | 1 | enable: 0 стоп, 1 разрешить команду |
0x0003 / 3 |
H003_control_mode |
R/W | 1 | режим AD_PARAM_ID_MODE_* |
0x0004 / 4 |
H004_control_reset_faults |
W | 1 | квитирование ошибок: записать 1 |
0x0005 / 5 |
H005_control_stop |
W | 1 | стоп: записать 1 |
0x0006 / 6 |
H006_control_locked_rotor_allowed |
R/W | 1 | разрешение locked-rotor теста |
0x0007 / 7 |
H007_control_pwm_duty_0p0001 |
R/W | 0.0001 |
лимит duty PWM |
0x0008 / 8 |
H008_limit_current_0p01_a |
R/W | 0.01 А |
предел тока |
0x0009 / 9 |
H009_limit_overvoltage_0p1_v |
R/W | 0.1 В |
предел перенапряжения DC-звена |
0x000A / 10 |
H010_limit_undervoltage_0p1_v |
R/W | 0.1 В |
предел недонапряжения, 0 отключает проверку |
0x000B / 11 |
H011_limit_speed_rpm |
R/W | 1 об/мин |
предел скорости |
0x000C / 12 |
H012_limit_temperature_0p1_c |
R/W | 0.1 град C |
предел температуры |
0x000D / 13 |
H013_control_rotation_freq_0p1_hz |
R/W | 0.1 Hz |
частота режима ROTATION_3HZ, firmware clamp 0.1..200.0 Hz |
0x000E / 14 |
H014_control_rotation_mod_0p0001 |
R/W | 0.0001 |
коэффициент модуляции режима ROTATION_3HZ |
0x000F / 15 |
H015_control_pwm_polarity_flags |
R/W | bitmask | полярность PWM: bit0=UH/VH/WH inverted, bit1=UL/VL/WL inverted |
Пример: duty 0.08 записать как 800 в 0x0007 / 7; для режима ROTATION_3HZ частоту 3.00 Hz записать как 30, 20.0 Hz как 200, 50.0 Hz как 500 в 0x000D / 13, модуляцию 0.35 как 3500 в 0x000E / 14. Полярность PWM: 0 - штатно, 1 - инвертировать UH/VH/WH, 2 - инвертировать UL/VL/WL, 3 - инвертировать все.
Статусы и ошибки
| Адрес hex / dec | Поле структуры | Доступ | Назначение |
|---|---|---|---|
0x0010 / 16 |
H016_status_mode |
R | фактический режим идентификации |
0x0011 / 17 |
H017_status_flags_lo |
R | AD_PARAM_ID_STATUS_*, младшее слово |
0x0012 / 18 |
H018_status_flags_hi |
R | AD_PARAM_ID_STATUS_*, старшее слово |
0x0013 / 19 |
H019_fault_flags_lo |
R | AD_PARAM_ID_FAULT_*, младшее слово |
0x0014 / 20 |
H020_fault_flags_hi |
R | AD_PARAM_ID_FAULT_*, старшее слово |
0x0015 / 21 |
H021_power_stage_allowed |
R | AD_ParamID_IsPowerStageAllowed() |
0x0016 / 22 |
H022_test_running |
R | тест активен |
0x0017 / 23 |
H023_inverter_pwm_running |
R | PWM TIM1 запущен |
0x0018 / 24 |
H024_inverter_service_output |
R | сервисный выход UH..WL/ALL |
0x0019 / 25 |
H025_inverter_id_stage |
R | стадия алгоритма измерения |
0x001A / 26 |
H026_control_pwm_timing_mode |
R/W | 0=up, 1=center |
0x001B / 27 |
H027_control_motor_control_type |
R/W | 0=AD/sine, 1=BLDC/6-step |
0x001E / 30 |
H030_control_rotation_ramp_time_ms |
R/W | ramp to rotation_frequency_Hz/rotation_modulation, 0 = default 3000 ms |
0x001F / 31 |
H031_control_reset_phase_current_peaks |
W | сброс накопленных пиков фазных токов: записать 1 |
Флаги статуса и ошибок оформлены enum в ad_parameter_identification.h: AD_ParamID_StatusFlag_t, AD_ParamID_FaultFlag_t, AD_MeasurementStatusFlag_t, AD_MotorParamValidFlag_t.
Расшифровка status_flags_lo (0x0011 / 17):
| Бит | Маска | Флаг | Значение |
|---|---|---|---|
| 0 | 0x0001 |
AD_PARAM_ID_STATUS_ACTIVE |
режим активен |
| 1 | 0x0002 |
AD_PARAM_ID_STATUS_POWER_TEST_BLOCKED |
силовой тест заблокирован |
| 2 | 0x0004 |
AD_PARAM_ID_STATUS_POWER_STAGE_ARMED |
силовая часть программно разрешена |
| 3 | 0x0008 |
AD_PARAM_ID_STATUS_FAULT_LATCHED |
авария защёлкнута |
| 4 | 0x0010 |
AD_PARAM_ID_STATUS_TIMEOUT |
истекло время теста |
| 5 | 0x0020 |
AD_PARAM_ID_STATUS_LOCKED_ROTOR_BLOCKED |
отдельный locked-rotor режим не разрешён |
| 6 | 0x0040 |
AD_PARAM_ID_STATUS_SAFETY_LIMITS_UNKNOWN |
не заданы пределы тока или напряжения |
| 7 | 0x0080 |
AD_PARAM_ID_STATUS_DATA_VALID |
есть актуальные измерения/данные |
| 8 | 0x0100 |
AD_PARAM_ID_STATUS_COMPLETE |
алгоритм завершён |
| 9 | 0x0200 |
AD_PARAM_ID_STATUS_PARTIAL_COMPLETE |
результат частичный, смотри valid_mask |
| 10 | 0x0400 |
AD_PARAM_ID_STATUS_STEP_FAILED |
один из этапов не получил достаточных условий |
| 11 | 0x0800 |
AD_PARAM_ID_STATUS_LOCKED_ROTOR_SKIPPED |
locked-rotor этап автоидентификации пропущен |
fault_flags в прошивке имеет тип uint32_t и отдается двумя holding-регистрами:
| Регистр | Поле структуры | Биты |
|---|---|---|
0x0013 / 19 |
H019_fault_flags_lo |
биты 0..15 |
0x0014 / 20 |
H020_fault_flags_hi |
биты 16..31 |
Расшифровка fault_flags_lo (0x0013 / 19):
| Бит | Маска | Флаг | Значение |
|---|---|---|---|
| 0 | 0x0001 |
AD_PARAM_ID_FAULT_OVERCURRENT |
превышен ток |
| 1 | 0x0002 |
AD_PARAM_ID_FAULT_OVERVOLTAGE |
превышено напряжение DC-звена |
| 2 | 0x0004 |
AD_PARAM_ID_FAULT_UNDERVOLTAGE |
напряжение DC-звена ниже лимита |
| 3 | 0x0008 |
AD_PARAM_ID_FAULT_OVERTEMPERATURE |
превышена температура |
| 4 | 0x0010 |
AD_PARAM_ID_FAULT_DRIVER |
ошибка драйвера силового каскада |
| 5 | 0x0020 |
AD_PARAM_ID_FAULT_EMERGENCY_STOP |
аварийный останов |
| 6 | 0x0040 |
AD_PARAM_ID_FAULT_TIMEOUT |
таймаут теста |
| 7 | 0x0080 |
AD_PARAM_ID_FAULT_NULL_INPUT |
нет входных измерений |
| 8..15 | 0xFF00 |
reserved | сейчас не используются |
fault_flags_hi (0x0014 / 20) сейчас зарезервирован и в норме равен 0. Если позже появятся флаги с битами 16..31, они будут отображаться здесь.
Измерения
| Адрес hex / dec | Поле структуры | Доступ | Масштаб | Назначение |
|---|---|---|---|---|
0x0020 / 32 |
H032_meas_ia_0p001_a |
R | 0.001 А |
ток фазы A, int16 |
0x0021 / 33 |
H033_meas_ib_0p001_a |
R | 0.001 А |
ток фазы B, int16 |
0x0022 / 34 |
H034_meas_ic_0p001_a |
R | 0.001 А |
ток фазы C, int16 |
0x0023 / 35 |
H035_input_volt_0p1_v |
R | 0.1 В |
DC-звено |
0x0024 / 36 |
H036_meas_temp_0p1_c |
R | 0.1 град C |
STM32 internal temperature |
0x0025 / 37 |
H037_meas_status_lo |
R | 1 | AD_MEAS_STATUS_*, младшее слово |
0x0026 / 38 |
H038_meas_status_hi |
R | 1 | AD_MEAS_STATUS_*, старшее слово |
0x0027 / 39 |
H039_meas_speed_rpm |
R | 1 rpm |
speed_rpm, int16 |
0x0028 / 40 |
H040_meas_ia_rms_0p001_A |
R | 0.001 A |
phase A RMS current |
0x0029 / 41 |
H041_meas_ib_rms_0p001_B |
R | 0.001 A |
phase B RMS current |
0x002A / 42 |
H042_meas_ic_rms_0p001_C |
R | 0.001 A |
phase C RMS current |
0x002B / 43 |
H043_meas_torque_0p001_nm |
R | 0.001 Nm |
estimated torque, int16 |
0x002C / 44 |
H044_meas_ia_peak_0p001_a |
R | 0.001 A |
phase A peak absolute current |
0x002D / 45 |
H045_meas_ib_peak_0p001_a |
R | 0.001 A |
phase B peak absolute current |
0x002E / 46 |
H046_meas_ic_peak_0p001_a |
R | 0.001 A |
phase C peak absolute current |
0x002F / 47 |
H047_meas_slip_0p01_percent |
R | 0.01 % |
induction motor slip, int16: (n_sync - speed_rpm) / n_sync * 100 |
Токи передаются как 16-битное значение в дополнительном коде. Например -1200 означает -1.2 А.
Результаты идентификации
| Адрес hex / dec | Поле структуры | Доступ | Масштаб | Назначение |
|---|---|---|---|---|
0x0030 / 48 |
H048_param_valid_mask_lo |
R | 1 | valid_mask, младшее слово |
0x0031 / 49 |
H049_param_valid_mask_hi |
R | 1 | valid_mask, старшее слово |
0x0032 / 50 |
H050_param_rs_mohm |
R | 1 мОм |
Rs_ohm |
0x0033 / 51 |
H051_param_ls_uh_lo |
R | 1 мкГн |
Ls_H, младшее слово uint32 |
0x0034 / 52 |
H052_param_ls_uh_hi |
R | 1 мкГн |
Ls_H, старшее слово uint32 |
0x0035 / 53 |
H053_param_ll_uh_lo |
R | 1 мкГн |
Ll_H, младшее слово uint32 |
0x0036 / 54 |
H054_param_ll_uh_hi |
R | 1 мкГн |
Ll_H, старшее слово uint32 |
0x0037 / 55 |
H055_param_rr_mohm |
R | 1 мОм |
Rr_ohm |
0x0038 / 56 |
H056_param_lr_uh_lo |
R | 1 мкГн |
Lr_H, младшее слово uint32 |
0x0039 / 57 |
H057_param_lr_uh_hi |
R | 1 мкГн |
Lr_H, старшее слово uint32 |
0x003A / 58 |
H058_param_lm_uh_lo |
R | 1 мкГн |
Lm_H, младшее слово uint32 |
0x003B / 59 |
H059_param_lm_uh_hi |
R | 1 мкГн |
Lm_H, старшее слово uint32 |
0x003C / 60 |
H060_param_j_nkgm2_lo |
R | 1 nkg*m2 |
J_kg_m2, младшее слово uint32 |
0x003D / 61 |
H061_param_j_nkgm2_hi |
R | 1 nkg*m2 |
J_kg_m2, старшее слово uint32 |
0x003E / 62 |
H062_param_b_nnms_lo |
R | 1 nNm*s |
B_Nm_s, младшее слово uint32 |
0x003F / 63 |
H063_param_b_nnms_hi |
R | 1 nNm*s |
B_Nm_s, старшее слово uint32 |
0x0040 / 64 |
H064_param_pole_pairs |
R/W | 1 pair |
motor pole pairs for synchronous speed, slip, and torque calculations |
0x0041 / 65 |
H065_param_phase_shunt_mohm |
R/W | 1 мОм |
сопротивление фазных шунтов; default 0.1 Ом = 100 |
Перед использованием результата обязательно проверить valid_mask.
Запуск теста через Modbus
Для запуска используй holding-регистры управления через функцию 0x06 Write Single Register или 0x10 Write Multiple Registers. Проверку состояния делай функцией 0x03 Read Holding Registers.
Если мастер работает в формате 4xxxx, прибавляй базу 40001: например 0x0002 / 2 это 40003, а 0x0007 / 7 это 40008.
Пиши enable (0x0002 / 2) последним. До этого нужно задать duty, лимиты и режим, иначе можно запустить предыдущий режим со старыми настройками.
Базовая последовательность:
1. Проверить связь:
read 0x0000 / 0, quantity 2
ожидаемо: 0xAD01, 0x0008
2. Остановить предыдущий тест:
write 0x0005 / 5 = 1
3. Сбросить latched fault, если был:
write 0x0004 / 4 = 1
4. Задать ограничения и duty:
write 0x0007 / 7 = 800 ; duty 0.08
write 0x0008 / 8 = 1000 ; ток 10.00 А
write 0x0009 / 9 = 600 ; перенапряжение 60.0 В
write 0x000A / 10 = 0 ; недонапряжение отключено
write 0x000B / 11 = 1000 ; скорость 1000 rpm
write 0x000C / 12 = 850 ; температура 85.0 град C
write 0x000D / 13 = 30 ; ROTATION_3HZ: частота 3.00 Hz
write 0x000E / 14 = 3500 ; ROTATION_3HZ: модуляция 0.35
write 0x000F / 15 = 1 ; PWM polarity flags
write 0x001A / 26 = 1 ; PWM timing: 0=up, 1=center
write 0x001B / 27 = 0 ; motor control: 0=AD/sine, 1=BLDC/6-step
write 0x001E / 30 = 3000 ; rotation ramp time, ms
write 0x0041 / 65 = 100 ; phase shunts 0.1 Ohm
5. Выбрать режим теста:
write 0x0003 / 3 = <mode>
6. Запустить:
write 0x0002 / 2 = 1
Ту же подготовку можно отправить одним 0x10 Write Multiple Registers, если пишешь непрерывный диапазон 0x0007..0x000F для duty/лимитов/параметров вращения/полярности. 0x001A..0x001B для up/center и AD/BLDC пишутся отдельным диапазоном, 0x001E для времени рампы - отдельным регистром. Режим и enable удобнее оставить отдельными командами: сначала 0x0003 / 3, затем 0x0002 / 2.
0x10 Write Multiple Registers validates the whole writable range first, stages all values, then applies the command once. If any value is invalid, none of the packet values are committed.
Режимы mode:
| Значение | Режим |
|---|---|
0 |
IDLE, останов |
1 |
STATOR_RESISTANCE |
2 |
NO_LOAD_MAGNETIZING |
3 |
LOCKED_ROTOR_LEAKAGE |
4 |
INERTIA_FRICTION |
5 |
DATA_LOGGING |
6 |
PWM_TEST_UH |
7 |
PWM_TEST_UL |
8 |
PWM_TEST_VH |
9 |
PWM_TEST_VL |
10 |
PWM_TEST_WH |
11 |
PWM_TEST_WL |
12 |
PWM_TEST_ALL |
13 |
AUTO_IDENTIFICATION |
14 |
ROTATION_3HZ |
Режимы 1, 2, 3, 4, 6..14 требуют силовой части. DATA_LOGGING (5) можно использовать как безопасный режим наблюдения без включения PWM.
Пример запуска PWM-теста верхнего ключа фазы U (UH) с duty 0.08:
write 0x0005 / 5 = 1
write 0x0004 / 4 = 1
write 0x0007 / 7 = 800
write 0x0003 / 3 = 6
write 0x0002 / 2 = 1
Пример запуска автоидентификации:
write 0x0005 / 5 = 1
write 0x0004 / 4 = 1
write 0x0007 / 7 = 800
write 0x0003 / 3 = 13
write 0x0002 / 2 = 1
Для locked-rotor теста дополнительно нужно разрешение:
write 0x0005 / 5 = 1
write 0x0004 / 4 = 1
write 0x0006 / 6 = 1
write 0x0007 / 7 = 800
write 0x0003 / 3 = 3
write 0x0002 / 2 = 1
Контроль после запуска:
read 0x0010 / 16, quantity 10
Смотри основные регистры:
| Адрес hex / dec | Что должно быть |
|---|---|
0x0010 / 16 |
фактический режим, должен совпасть с выбранным mode |
0x0011 / 17 |
младшее слово AD_PARAM_ID_STATUS_* |
0x0013 / 19 |
младшее слово AD_PARAM_ID_FAULT_*, норма 0 |
0x0015 / 21 |
1, если силовой каскад разрешён |
0x0016 / 22 |
1, если тест активен |
0x0017 / 23 |
1, если PWM TIM1 реально запущен |
0x0019 / 25 |
стадия алгоритма измерения |
Если читаешь через структуру в отладчике, это те же значения:
MB_REGS->holding.H016_status_mode
MB_REGS->holding.H017_status_flags_lo
MB_REGS->holding.H019_fault_flags_lo
MB_REGS->holding.H022_test_running
Полезные биты статуса 0x0011:
| Бит | Маска | Значение |
|---|---|---|
| 0 | 0x0001 |
тест активен |
| 1 | 0x0002 |
силовой тест заблокирован, прошивка ушла в DATA_LOGGING |
| 2 | 0x0004 |
power stage armed |
| 3 | 0x0008 |
ошибка защёлкнута, нужен сброс через 0x0004 / 4 |
| 4 | 0x0010 |
timeout |
| 5 | 0x0020 |
locked-rotor тест заблокирован |
| 6 | 0x0040 |
не заданы безопасные лимиты |
| 8 | 0x0100 |
тест завершён |
Расшифровка fault_flags_lo и fault_flags_hi приведена выше в разделе “Статусы и ошибки”.
При любой ошибке сначала останови тест, затем квитируй fault:
write 0x0005 / 5 = 1
write 0x0004 / 4 = 1
Останов:
write 0x0005 / 5 = 1
Или:
write 0x0002 / 2 = 0
Для реального PWM нужен build-макрос AD_PROJECT_POWER_TEST_ENABLE=1. Если он равен 0, команды силовых тестов не включат ключи: прошивка выставит AD_PARAM_ID_STATUS_POWER_TEST_BLOCKED и перейдёт в безопасный режим DATA_LOGGING.
Быстрые сценарии
Квитировать ошибку:
write 0x0004 / 4 = 1
Запустить PWM-тест UH с duty 0.08:
write 0x0007 / 7 = 800
write 0x0003 / 3 = 6
write 0x0002 / 2 = 1
Запустить автоидентификацию текущего уровня:
write 0x0007 / 7 = 800
write 0x0003 / 3 = 13
write 0x0002 / 2 = 1
Остановить:
write 0x0005 / 5 = 1
Для физического PWM всё равно нужен build-макрос AD_PROJECT_POWER_TEST_ENABLE=1, иначе команда будет заблокирована защитой проекта.