30#ifndef VORTEX_TMC9660_DRIVE_OBSERVABILITY_H_
31#define VORTEX_TMC9660_DRIVE_OBSERVABILITY_H_
40#include "core/hf-core-drivers/external/hf-tmc9660-driver/inc/tmc9660.hpp"
44using Param = tmc9660::tmcl::Parameters;
57 [[nodiscard]]
bool ok() const noexcept {
return status == ReplyCode::REPLY_OK; }
65template <
typename Driver,
bool kWrite>
66size_t RunBatch(Driver& driver, std::span<ParamAccess> items)
noexcept {
67 using DriverAccess =
typename Driver::ParameterAccess;
68 std::array<DriverAccess, kBatchChunk> buf{};
70 for (
size_t base = 0; base < items.size(); base +=
kBatchChunk) {
71 const size_t n = std::min(
kBatchChunk, items.size() - base);
72 for (
size_t i = 0; i < n; ++i) {
73 buf[i] = DriverAccess{};
74 buf[i].id = items[base + i].id;
75 buf[i].value = items[base + i].value;
76 buf[i].motor = items[base + i].motor;
78 const std::span<DriverAccess> chunk(buf.data(), n);
79 if constexpr (kWrite) {
80 (void)driver.writeParameters(chunk);
82 (void)driver.readParameters(chunk);
84 for (
size_t i = 0; i < n; ++i) {
86 out.
status = buf[i].status;
87 if constexpr (!kWrite) {
89 out.
value = buf[i].value;
92 ok += out.
ok() ? 1 : 0;
101template <
typename Driver>
102size_t BatchRead(Driver& driver, std::span<ParamAccess> items)
noexcept {
103 return detail::RunBatch<Driver, false>(driver, items);
107template <
typename Driver>
108size_t BatchWrite(Driver& driver, std::span<ParamAccess> items)
noexcept {
109 return detail::RunBatch<Driver, true>(driver, items);
141 Param::ACTUAL_POSITION, Param::ACTUAL_VELOCITY,
142 Param::TORQUE_FLUX_COMBINED_ACTUAL_VALUES,
143 Param::GENERAL_STATUS_FLAGS, Param::GENERAL_ERROR_FLAGS, Param::GDRV_ERROR_FLAGS, Param::ADC_STATUS_FLAGS,
144 Param::SUPPLY_VOLTAGE, Param::CHIP_TEMPERATURE, Param::EXTERNAL_TEMPERATURE,
145 Param::ACTUAL_TOTAL_MOTOR_CURRENT,
146 Param::RAMP_VELOCITY, Param::RAMP_POSITION,
147 Param::VOLTAGE_D_Q_COMBINED_ACTUAL_VALUES,
148 Param::IIT_SUM_1, Param::IIT_SUM_2,
167 std::array<uint32_t, kTelemetryFieldCount>
raw{};
179 return (
valid_mask >>
static_cast<unsigned>(f)) & 1U;
183 const uint32_t s =
field_seq[
static_cast<size_t>(f)];
184 return s == 0 ? UINT32_MAX :
sequence - s;
214 auto bit = [](
TelemetryField f)
constexpr {
return 1U <<
static_cast<unsigned>(f); };
236template <
typename Driver>
238 const uint32_t seq = snap.sequence + 1;
240 std::array<ParamAccess, kTelemetryFieldCount> items{};
241 std::array<uint8_t, kTelemetryFieldCount> field_of{};
244 if ((mask >> f) & 1U) {
246 field_of[n] =
static_cast<uint8_t
>(f);
251 const size_t ok =
BatchRead(driver, std::span<ParamAccess>(items.data(), n));
255 snap.requested_mask = mask;
257 for (
size_t i = 0; i < n; ++i) {
259 const size_t f = field_of[i];
260 snap.raw[f] = items[i].value;
261 snap.field_seq[f] = seq;
262 snap.valid_mask |= 1U << f;
265 snap.timestamp_us = t1;
266 snap.batch_duration_us =
static_cast<uint32_t
>(t1 - t0);
267 snap.accesses =
static_cast<uint8_t
>(n);
268 snap.failed =
static_cast<uint8_t
>(n - ok);
335 uint32_t adc)
noexcept {
336 using tmc9660::tmcl::GeneralErrorFlags;
337 using tmc9660::tmcl::GeneralStatusFlags;
338 auto s = [status](GeneralStatusFlags f) {
return (status &
static_cast<uint32_t
>(f)) != 0; };
339 auto e = [gen_err](GeneralErrorFlags f) {
return (gen_err &
static_cast<uint32_t
>(f)) != 0; };
340 auto nib = [](uint32_t w,
unsigned shift) {
return static_cast<uint8_t
>((w >> shift) & 0xFU); };
344 d.general_errors = gen_err;
345 d.gate_driver_errors = gdrv;
348 d.regulation_stopped = s(GeneralStatusFlags::REGULATION_STOPPED);
349 d.adc_offset_calibrated = s(GeneralStatusFlags::ADC_OFFSET_CALIBRATED);
350 d.iit1_active = s(GeneralStatusFlags::IIT_1_ACTIVE);
351 d.iit2_active = s(GeneralStatusFlags::IIT_2_ACTIVE);
352 d.config_loaded = s(GeneralStatusFlags::CONFIG_LOADED);
354 d.chip_reset_detected = e(GeneralErrorFlags::WATCHDOG_EVENT);
355 d.hall_invalid = e(GeneralErrorFlags::HALL_ERROR);
356 d.ext_overtemperature = e(GeneralErrorFlags::EXT_TEMP_EXCEEDED);
357 d.chip_overtemperature = e(GeneralErrorFlags::CHIP_TEMP_EXCEEDED);
358 d.iit1_exceeded = e(GeneralErrorFlags::I2T_1_EXCEEDED);
359 d.iit2_exceeded = e(GeneralErrorFlags::I2T_2_EXCEEDED);
360 d.ext_temperature_warning = e(GeneralErrorFlags::EXT_TEMP_WARNING);
361 d.supply_overvoltage_warning = e(GeneralErrorFlags::SUPPLY_OVERVOLTAGE_WARNING);
362 d.supply_undervoltage_warning = e(GeneralErrorFlags::SUPPLY_UNDERVOLTAGE_WARNING);
363 d.adc_input_overvoltage = e(GeneralErrorFlags::ADC_IN_OVERVOLTAGE);
364 d.fault_retry_happened = e(GeneralErrorFlags::FAULT_RETRY_HAPPENED);
365 d.fault_retries_failed = e(GeneralErrorFlags::FAULT_RETRIES_FAILED);
366 d.chip_temperature_warning = e(GeneralErrorFlags::CHIP_TEMP_WARNING);
367 d.heartbeat_stopped = e(GeneralErrorFlags::HEARTBEAT_STOPPED);
369 d.overcurrent_low_side = nib(gdrv, 0);
370 d.vgs_short_low_side =
static_cast<uint8_t
>(nib(gdrv, 4) | nib(gdrv, 8));
371 d.bootstrap_undervoltage = nib(gdrv, 12);
372 d.overcurrent_high_side = nib(gdrv, 16);
373 d.vgs_short_high_side =
static_cast<uint8_t
>(nib(gdrv, 20) | nib(gdrv, 24));
374 d.gate_driver_undervoltage = (gdrv & 0xE0000000U) != 0;
376 d.adc_clipped =
static_cast<uint16_t
>(adc & 0x3FFFU);
378 const bool fault = gdrv != 0 || d.fault_retries_failed || d.ext_overtemperature || d.chip_overtemperature ||
379 d.heartbeat_stopped || d.iit1_exceeded || d.iit2_exceeded;
380 const bool warning = d.chip_reset_detected || d.hall_invalid || d.ext_temperature_warning ||
381 d.supply_overvoltage_warning || d.supply_undervoltage_warning || d.adc_input_overvoltage ||
382 d.fault_retry_happened || d.chip_temperature_warning || d.config_loaded ||
389template <
typename Driver>
391 std::array<ParamAccess, 4> items{};
392 items[0].id = Param::GENERAL_STATUS_FLAGS;
393 items[1].id = Param::GENERAL_ERROR_FLAGS;
394 items[2].id = Param::GDRV_ERROR_FLAGS;
395 items[3].id = Param::ADC_STATUS_FLAGS;
397 (void)
BatchRead(driver, std::span<ParamAccess>(items));
399 out =
DecodeDriveStatus(items[0].value, items[1].value, items[2].value, items[3].value);
401 for (
size_t i = 0; i < items.size(); ++i) {
402 out.words_valid =
static_cast<uint8_t
>(out.words_valid | (items[i].ok() ? (1U << i) : 0U));
404 out.batch_duration_us =
static_cast<uint32_t
>(t1 - t0);
405 return out.words_valid == 0xFU;
415template <
typename Driver>
417 uint32_t adc_status)
noexcept {
418 std::array<ParamAccess, 3> items{};
420 if (general_errors != 0) items[n++] =
ParamAccess{Param::GENERAL_ERROR_FLAGS, general_errors, 0, {}};
421 if (gate_driver_errors != 0) items[n++] =
ParamAccess{Param::GDRV_ERROR_FLAGS, gate_driver_errors, 0, {}};
422 if (adc_status != 0) items[n++] =
ParamAccess{Param::ADC_STATUS_FLAGS, adc_status, 0, {}};
423 return BatchWrite(driver, std::span<ParamAccess>(items.data(), n)) == n;
462template <
typename Driver,
typename Pred>
465 auto note_bad = [&r](
Param id,
ReplyCode status, uint32_t readback) {
466 if (r.rejected + r.mismatches == 0) {
468 r.first_bad_status = status;
469 r.first_bad_readback = readback;
472 std::array<ParamAccess, kBatchChunk> chunk{};
474 while (i < image.size()) {
478 if (take(image[i])) {
479 chunk[n++] =
ParamAccess{image[i].
id, image[i].value, 0, ReplyCode::REPLY_CHKERR};
487 std::array<ParamAccess, kBatchChunk> to_write{};
489 if (opt.differential) {
490 std::array<ParamAccess, kBatchChunk> current{};
491 for (
size_t j = 0; j < n; ++j) current[j].
id = chunk[j].
id;
492 (void)
BatchRead(driver, std::span<ParamAccess>(current.data(), n));
493 for (
size_t j = 0; j < n; ++j) {
494 if (current[j].ok() && current[j].value == chunk[j].value) {
497 to_write[w++] = chunk[j];
501 for (
size_t j = 0; j < n; ++j) to_write[w++] = chunk[j];
503 (void)
BatchWrite(driver, std::span<ParamAccess>(to_write.data(), w));
504 r.written =
static_cast<uint16_t
>(r.written + w);
505 for (
size_t j = 0; j < w; ++j) {
506 if (!to_write[j].ok()) {
507 note_bad(to_write[j].
id, to_write[j].status, 0);
513 for (
size_t j = 0; j < n; ++j) {
514 to_write[j] =
ParamAccess{chunk[j].
id, 0, 0, ReplyCode::REPLY_CHKERR};
516 (void)
BatchRead(driver, std::span<ParamAccess>(to_write.data(), n));
517 for (
size_t j = 0; j < n; ++j) {
518 if (!to_write[j].ok() || to_write[j].value != chunk[j].value) {
519 note_bad(chunk[j].
id, to_write[j].status, to_write[j].value);
536template <
typename Driver>
539 r.
entries =
static_cast<uint16_t
>(std::min<size_t>(image.size(), UINT16_MAX));
The board binding: the only way the Vortex managers reach MCU peripherals.
uint64_t MonotonicMicros() noexcept
Monotonic microseconds since boot, at the MCU's high-resolution timer (not the RTOS tick).
Definition VortexBoardBindEsp32c6.cpp:317
void ApplyPhase(Driver &driver, std::span< const ParamWrite > image, ApplyOptions opt, Pred take, ApplyResult &r) noexcept
Apply the image entries selected by take, in image order, kBatchChunk at a time.
Definition Tmc9660DriveObservability.h:463
size_t RunBatch(Driver &driver, std::span< ParamAccess > items) noexcept
Definition Tmc9660DriveObservability.h:66
Definition Tmc9660DriveObservability.h:42
constexpr size_t kBatchChunk
Elements per pipelined chunk (bounds the stack buffer; the driver chunks again internally).
Definition Tmc9660DriveObservability.h:61
MotorDriveDiagnostics DecodeDriveStatus(uint32_t status, uint32_t gen_err, uint32_t gdrv, uint32_t adc) noexcept
Decode the four status words into named conditions and a severity.
Definition Tmc9660DriveObservability.h:334
bool ClearLatchedFlags(Driver &driver, uint32_t general_errors, uint32_t gate_driver_errors, uint32_t adc_status) noexcept
Clear latched (write-1-to-clear) bits of the error words in one batch.
Definition Tmc9660DriveObservability.h:416
size_t BatchWrite(Driver &driver, std::span< ParamAccess > items) noexcept
Write a batch (SAP, pipelined; SAP is idempotent).
Definition Tmc9660DriveObservability.h:108
TelemetrySet
Which signals one ReadTelemetry() call fetches.
Definition Tmc9660DriveObservability.h:152
@ Cyclic
The cyclic fixed slots (§2.2): 144, 125, 331, 289, and 299 / 300 on alternate calls (5 accesses).
@ Status
Status, error and power/thermal words: 289, 299, 300, 301, 290, 296, 293, 231, 226,...
constexpr uint32_t TelemetryRequestMask(TelemetrySet set, uint32_t next_sequence) noexcept
Fields a set asks for, given the snapshot's next sequence number (Cyclic alternates 299/300).
Definition Tmc9660DriveObservability.h:213
tmc9660::tmcl::Parameters Param
Definition Tmc9660DriveObservability.h:44
tmc9660::tmcl::ReplyCode ReplyCode
Definition Tmc9660DriveObservability.h:45
ApplyResult ApplyParameterImage(Driver &driver, std::span< const ParamWrite > image, ApplyOptions opt) noexcept
Apply a parameter image: MOTOR_TYPE first, then the rest in image order.
Definition Tmc9660DriveObservability.h:537
DriveSeverity
How serious the decoded status words are.
Definition Tmc9660DriveObservability.h:277
@ Warning
Thresholds crossed or events latched; the bridge may still run.
@ Fault
The chip stopped (or must stop) driving: gate-driver fault, retries failed, overtemperature.
@ Ok
Nothing set that needs attention.
bool ReadDriveDiagnostics(Driver &driver, MotorDriveDiagnostics &out) noexcept
Read the four status words in one batch and decode them.
Definition Tmc9660DriveObservability.h:390
constexpr size_t kTelemetryFieldCount
Definition Tmc9660DriveObservability.h:137
TelemetryField
The telemetry signals a snapshot carries (design §1.3, tiers C/F/S).
Definition Tmc9660DriveObservability.h:117
@ TotalMotorCurrent
A:231, mA.
@ ActualPosition
A:144, counts.
@ GeneralErrors
A:299 (write-1-to-clear latches)
@ ExternalTemperature
A:293, raw AIN3 (NTC curve on the host)
@ ChipTemperature
A:296, raw (°C = v·0.01615 − 268.15)
@ VoltageDqActual
A:332, packed Ud/Uq, unscaled.
@ ActualVelocity
A:125, chip velocity units.
@ TorqueFluxActual
A:331, packed Iq (high 16) / Id (low 16), unscaled.
@ SupplyVoltage
A:290, 0.1 V.
size_t ReadTelemetry(Driver &driver, TelemetrySet set, MotorTelemetrySnapshot &snap) noexcept
Refresh snap from one pipelined batch.
Definition Tmc9660DriveObservability.h:237
constexpr std::array< Param, kTelemetryFieldCount > kTelemetryParam
TMC9660 parameter behind each telemetry field.
Definition Tmc9660DriveObservability.h:140
size_t BatchRead(Driver &driver, std::span< ParamAccess > items) noexcept
Read a batch (GAP, pipelined).
Definition Tmc9660DriveObservability.h:102
How ApplyParameterImage() works.
Definition Tmc9660DriveObservability.h:437
bool differential
Read the chip first and write only entries whose value differs (§4.5)
Definition Tmc9660DriveObservability.h:438
bool verify
Read every entry back after writing and compare (§4.4)
Definition Tmc9660DriveObservability.h:439
What ApplyParameterImage() did.
Definition Tmc9660DriveObservability.h:443
ReplyCode first_bad_status
Definition Tmc9660DriveObservability.h:450
Param first_bad
First rejected or mismatched parameter.
Definition Tmc9660DriveObservability.h:449
uint16_t written
SAPs sent (after the differential skip)
Definition Tmc9660DriveObservability.h:445
uint16_t skipped
Already equal on the chip.
Definition Tmc9660DriveObservability.h:446
uint16_t rejected
SAPs the chip refused (status != REPLY_OK)
Definition Tmc9660DriveObservability.h:447
uint32_t first_bad_readback
Definition Tmc9660DriveObservability.h:451
uint16_t mismatches
Read back different from the image.
Definition Tmc9660DriveObservability.h:448
bool ok() const noexcept
Definition Tmc9660DriveObservability.h:452
uint16_t entries
Entries in the image.
Definition Tmc9660DriveObservability.h:444
The four TMC9660 status words, decoded (design §1.5). Raw words are kept for anything not named.
Definition Tmc9660DriveObservability.h:284
bool adc_input_overvoltage
b21
Definition Tmc9660DriveObservability.h:308
bool iit1_active
b18
Definition Tmc9660DriveObservability.h:294
uint16_t adc_clipped
b0..13: I0..I3, U0..U3, AIN0..AIN3, VM, TEMP
Definition Tmc9660DriveObservability.h:323
DriveSeverity severity
Definition Tmc9660DriveObservability.h:325
bool chip_temperature_warning
b24
Definition Tmc9660DriveObservability.h:311
bool fault_retries_failed
b23
Definition Tmc9660DriveObservability.h:310
uint8_t words_valid
Bit 0..3 = 289/299/300/301 read OK.
Definition Tmc9660DriveObservability.h:289
bool ext_temperature_warning
b18
Definition Tmc9660DriveObservability.h:305
uint8_t bootstrap_undervoltage
b12..15
Definition Tmc9660DriveObservability.h:319
bool adc_offset_calibrated
b11
Definition Tmc9660DriveObservability.h:293
bool supply_overvoltage_warning
b19
Definition Tmc9660DriveObservability.h:306
bool supply_undervoltage_warning
b20
Definition Tmc9660DriveObservability.h:307
bool chip_reset_detected
b9 watchdog event: the TMC9660 reset; re-apply the parameter image
Definition Tmc9660DriveObservability.h:299
bool spi_stats_valid
spi_stats filled (SPI transport only)
Definition Tmc9660DriveObservability.h:329
uint32_t gate_driver_errors
A:300.
Definition Tmc9660DriveObservability.h:287
uint32_t general_status
A:289.
Definition Tmc9660DriveObservability.h:285
uint8_t vgs_short_high_side
b20..27, folded per phase
Definition Tmc9660DriveObservability.h:318
uint8_t overcurrent_high_side
b16..19
Definition Tmc9660DriveObservability.h:316
bool iit1_exceeded
b16
Definition Tmc9660DriveObservability.h:303
bool gate_driver_undervoltage
b29..31 (UV, low voltage, supply UV)
Definition Tmc9660DriveObservability.h:320
uint8_t vgs_short_low_side
b4..11 (discharge b4..7 | charge b8..11), folded per phase
Definition Tmc9660DriveObservability.h:317
uint32_t adc_status
A:301.
Definition Tmc9660DriveObservability.h:288
bool iit2_exceeded
b17
Definition Tmc9660DriveObservability.h:304
tmc9660::TmclSpiStats spi_stats
TMCL SPI adapter counters (retries, NOT_READY, transport errors)
Definition Tmc9660DriveObservability.h:330
bool config_loaded
b5: config loaded from TMC NVM (not used on Vortex; unexpected)
Definition Tmc9660DriveObservability.h:296
bool regulation_stopped
b0: not regulating (the fault handler reverts to SYSTEM_OFF)
Definition Tmc9660DriveObservability.h:292
bool chip_overtemperature
b14
Definition Tmc9660DriveObservability.h:302
uint32_t general_errors
A:299.
Definition Tmc9660DriveObservability.h:286
bool hall_invalid
b5
Definition Tmc9660DriveObservability.h:300
uint8_t overcurrent_low_side
b0..3
Definition Tmc9660DriveObservability.h:315
uint32_t batch_duration_us
Time the status batch took.
Definition Tmc9660DriveObservability.h:328
bool ext_overtemperature
b13
Definition Tmc9660DriveObservability.h:301
bool fault_retry_happened
b22
Definition Tmc9660DriveObservability.h:309
bool heartbeat_stopped
b26: the independent stop path acted
Definition Tmc9660DriveObservability.h:312
bool iit2_active
b19
Definition Tmc9660DriveObservability.h:295
Caller-owned snapshot, refreshed in place by MotorController::ReadTelemetry().
Definition Tmc9660DriveObservability.h:166
uint64_t timestamp_us
Monotonic time at the end of the latest batch.
Definition Tmc9660DriveObservability.h:172
uint32_t sequence
Incremented by every ReadTelemetry() call.
Definition Tmc9660DriveObservability.h:169
float ChipTemperatureC() const noexcept
Definition Tmc9660DriveObservability.h:204
bool Valid(TelemetryField f) const noexcept
Definition Tmc9660DriveObservability.h:178
int32_t ActualPosition() const noexcept
Definition Tmc9660DriveObservability.h:187
uint32_t Age(TelemetryField f) const noexcept
Reads since field f was last good (0 = in the latest call); UINT32_MAX if never read.
Definition Tmc9660DriveObservability.h:182
int16_t TorqueCurrentRaw() const noexcept
Iq from the packed 331 word (high 16 bits), unscaled until calibrated (design §1.4).
Definition Tmc9660DriveObservability.h:194
int32_t TotalMotorCurrentMa() const noexcept
Definition Tmc9660DriveObservability.h:207
float SupplyVoltageV() const noexcept
Definition Tmc9660DriveObservability.h:201
uint32_t Raw(TelemetryField f) const noexcept
Definition Tmc9660DriveObservability.h:177
uint32_t requested_mask
Bit f set if the latest call asked for field f.
Definition Tmc9660DriveObservability.h:171
uint8_t accesses
TMCL accesses in the latest batch.
Definition Tmc9660DriveObservability.h:174
uint8_t failed
... of which were not answered REPLY_OK
Definition Tmc9660DriveObservability.h:175
uint32_t valid_mask
Bit f set if field f was read OK in the latest call.
Definition Tmc9660DriveObservability.h:170
std::array< uint32_t, kTelemetryFieldCount > field_seq
Sequence of the last good read (0 = never)
Definition Tmc9660DriveObservability.h:168
int32_t ActualVelocity() const noexcept
Definition Tmc9660DriveObservability.h:190
int16_t FluxCurrentRaw() const noexcept
Id from the packed 331 word (low 16 bits), unscaled until calibrated.
Definition Tmc9660DriveObservability.h:198
uint32_t batch_duration_us
Time the latest batch took (all accesses)
Definition Tmc9660DriveObservability.h:173
std::array< uint32_t, kTelemetryFieldCount > raw
Last good value per field.
Definition Tmc9660DriveObservability.h:167
One axis-parameter access in a batch (MCU- and transport-neutral copy of the driver's type).
Definition Tmc9660DriveObservability.h:52
uint32_t value
Write: value to set. Read: value read back.
Definition Tmc9660DriveObservability.h:54
ReplyCode status
TMCL status of this element.
Definition Tmc9660DriveObservability.h:56
Param id
Axis parameter.
Definition Tmc9660DriveObservability.h:53
uint8_t motor
Axis (0 on the TMC9660)
Definition Tmc9660DriveObservability.h:55
bool ok() const noexcept
Definition Tmc9660DriveObservability.h:57
One entry of a parameter image (design §4.1, the TMC image).
Definition Tmc9660DriveObservability.h:431
Param id
Definition Tmc9660DriveObservability.h:432
uint32_t value
Definition Tmc9660DriveObservability.h:433