ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
tmc22xx.cpp
Go to the documentation of this file.
1#include "tmc22xx.h"
3#include "esphome/core/log.h"
4
5#include <cmath>
6#include <cstring>
7#include <numbers>
8
9namespace esphome::tmc22xx {
10
11ESPHOME_LOG_TAG(TAG, "tmc22xx");
12
13static constexpr uint8_t SYNC_BYTE = 0x05;
14static constexpr uint8_t MASTER_ADDRESS = 0xFF;
15static constexpr uint8_t WRITE_BIT = 0x80;
16static constexpr size_t READ_REQUEST_SIZE = 4;
17static constexpr size_t DATAGRAM_SIZE = 8;
18static constexpr uint8_t DEFAULT_TOFF = 3;
19static constexpr float SENSE_RESISTOR_OFFSET = 0.02f; // Ohm, added to RSENSE in the datasheet current formula
20static constexpr float INTERNAL_RSENSE_OHM = 0.17f; // Equivalent sense resistance when using internal sensing
21// INDEX pulses are counted once per loop, so near the target the speed is limited to cover the remaining
22// distance in no less than this time. A slow loop pass then cannot run far past the target.
23static constexpr float APPROACH_TIME_S = 0.02f;
24// Skip VACTUAL writes that change the speed by less than 1/16, each write blocks the loop for the UART transfer
25static constexpr int32_t VACTUAL_CHANGE_DIVISOR = 16;
26static constexpr uint32_t STATUS_POLL_INTERVAL_MS = 1000;
27// While the driver does not answer, for example without motor supply, only poll every 10 s; each failed read
28// blocks the loop for the UART timeout
29static constexpr uint8_t UNREACHABLE_POLL_SKIP = 9;
30static constexpr uint32_t GSTAT_FLAGS = 0x07; // reset, drv_err, uv_cp
31static constexpr uint32_t DRV_STATUS_FLAGS = 0x0FFF;
32
33static const LogString *driver_event_to_string(DriverEvent event) {
34 switch (event) {
36 return LOG_STR("Driver reset");
38 return LOG_STR("Driver error");
40 return LOG_STR("Charge pump undervoltage");
42 return LOG_STR("Overtemperature prewarning");
44 return LOG_STR("Overtemperature shutdown");
46 return LOG_STR("Short to ground on phase A");
48 return LOG_STR("Short to ground on phase B");
50 return LOG_STR("Low side short on phase A");
52 return LOG_STR("Low side short on phase B");
54 return LOG_STR("Open load on phase A");
56 return LOG_STR("Open load on phase B");
58 return LOG_STR("Temperature above 120C");
60 return LOG_STR("Temperature above 143C");
62 return LOG_STR("Temperature above 150C");
64 return LOG_STR("Temperature above 157C");
65 default:
66 return LOG_STR("Unknown");
67 }
68}
69
70// CRC-8 with polynomial 0x07 over the datagram bits in the order they go out on the wire, LSB first
71static uint8_t datagram_crc(const uint8_t *data, size_t len) {
72 uint8_t crc = 0;
73 for (size_t i = 0; i < len; i++) {
74 const uint8_t reflected = reverse_bits(data[i]);
75 crc = crc8(&reflected, 1, crc, 0x07, true);
76 }
77 return crc;
78}
79
80// Index into RESTORED_REGISTERS, or -1 when the register is not restored after a driver reset
81static int restored_index(uint8_t reg) {
82 for (size_t i = 0; i < RESTORED_REGISTERS.size(); i++) {
83 if (RESTORED_REGISTERS[i] == reg)
84 return static_cast<int>(i);
85 }
86 return -1;
87}
88
89void IRAM_ATTR IndexPulseStore::gpio_intr(IndexPulseStore *arg) { arg->pulses = arg->pulses + arg->direction; }
90
91void IRAM_ATTR DiagPinStore::gpio_intr(DiagPinStore *arg) { arg->triggered = true; }
92
94 if (this->enn_pin_ != nullptr) {
95 this->enn_pin_->setup();
96 this->enn_pin_->digital_write(true);
97 }
98
99 auto version = this->read_field(VERSION);
100 if (!version.has_value()) {
101 ESP_LOGE(TAG, "No response from driver at address %u", this->address_);
102 this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL));
103 return;
104 }
105 this->version_ = *version;
106 if (this->version_ != this->expected_version()) {
107 ESP_LOGW(TAG, "Unexpected IC version 0x%02X", this->version_);
108 }
109
110 this->restore_registers();
111 this->write_register(REG_GSTAT, GSTAT_FLAGS);
112 this->apply_config_();
113 // Currents live in a write-only register, so later changes survive a driver reset through its cached value
114 if (!std::isnan(this->initial_run_current_))
116 if (!std::isnan(this->initial_hold_current_))
118
119 if (this->index_pin_ != nullptr) {
120 this->index_pin_->setup();
122 } else {
123 this->step_pin_->setup();
124 this->step_pin_->digital_write(false);
125 this->dir_pin_->setup();
126 this->dir_pin_->digital_write(false);
127 }
128
129 if (this->diag_pin_ != nullptr) {
130 this->diag_pin_->setup();
132 }
133 // Polling notices a driver reset, and conditions that clear without a DIAG edge
134 this->set_interval(STATUS_POLL_INTERVAL_MS, [this]() {
135 if (this->status_poll_skip_ > 0) {
136 this->status_poll_skip_--;
137 return;
138 }
139 if (!this->update_status_())
140 this->status_poll_skip_ = UNREACHABLE_POLL_SKIP;
141 });
142
143 this->set_enabled(true);
144}
145
147 this->write_register(REG_IHOLD_IRUN, this->ihold_irun_);
148 this->write_register(REG_TPOWERDOWN, this->tpowerdown_);
149 this->write_register(REG_TPWMTHRS, this->tpwmthrs_);
150 this->write_register(REG_VACTUAL, 0);
151 for (size_t i = 0; i < RESTORED_REGISTERS.size(); i++) {
152 if ((this->restored_written_ & (1u << i)) != 0)
153 this->write_register(RESTORED_REGISTERS[i], this->restored_values_[i]);
154 }
155}
156
158 this->write_field(PDN_DISABLE, true);
159 this->write_field(MSTEP_REG_SELECT, true);
160 this->write_field(INTERNAL_RSENSE, std::isnan(this->rsense_));
161 this->write_field(I_SCALE_ANALOG, this->analog_current_scale_);
162 if (this->vsense_.has_value())
163 this->write_field(VSENSE, *this->vsense_);
164 if (this->ottrim_.has_value())
165 this->write_field(OTTRIM, *this->ottrim_);
166 this->vsense_active_ = this->read_field(VSENSE).value_or(0);
167
168 // TOFF=0 disables the outputs, keep the current value so it can be restored on enable
169 auto toff = this->read_field(TOFF);
170 if (toff.has_value() && *toff != 0)
171 this->toff_ = *toff;
172
174
175 if (this->index_pin_ != nullptr) {
176 // INDEX toggles on every step of the internal pulse generator that VACTUAL drives
177 this->write_field(INDEX_OTPW, false);
178 this->write_field(INDEX_STEP, true);
179 } else {
180 // Step on both edges, so every step pin toggle is one step
181 this->write_field(DEDGE, true);
182 this->write_field(MULTISTEP_FILT, false);
183 }
184}
185
187 auto gstat = this->read_register(REG_GSTAT);
188 if (!gstat.has_value())
189 return false;
190 uint32_t drv_status = 0;
191 // DRV_STATUS only feeds on_status and the DIAG check, skip the transfer when neither is used
192 if (this->diag_pin_ != nullptr || !this->status_callback_.empty()) {
193 auto value = this->read_register(REG_DRV_STATUS);
194 if (!value.has_value())
195 return false;
196 drv_status = *value;
197 }
198 // GSTAT flags stay set until written with 1; they are set again while the condition lasts
199 if ((*gstat & GSTAT_FLAGS) != 0)
200 this->write_register(REG_GSTAT, *gstat & GSTAT_FLAGS);
201
202 uint16_t status = (*gstat & GSTAT_FLAGS) | ((drv_status & DRV_STATUS_FLAGS) << 3);
203 // Restore before reporting, so an on_status automation for the reset sees and can change the restored settings
204 if ((status & (1u << DRIVER_EVENT_RESET)) != 0) {
205 ESP_LOGW(TAG, "Restoring settings after driver reset");
206 this->restore_registers();
207 // The motor stopped with the reset, ramp up again from standstill
208 this->current_speed_ = 0.0f;
209 this->set_enabled(this->enabled_);
210 }
211 uint16_t changed = status ^ this->status_;
212 this->status_ = status;
213 for (uint8_t event = 0; event < DRIVER_EVENT_COUNT; event++) {
214 if ((changed & (1u << event)) == 0)
215 continue;
216 bool active = (status & (1u << event)) != 0;
217 // Open load and temperature levels are informational, the driver keeps running
218 if (active && event < DRIVER_EVENT_OPEN_LOAD_A) {
219 ESP_LOGW(TAG, "%s", LOG_STR_ARG(driver_event_to_string(static_cast<DriverEvent>(event))));
220 } else {
221 ESP_LOGD(TAG, "%s: %s", LOG_STR_ARG(driver_event_to_string(static_cast<DriverEvent>(event))),
222 active ? LOG_STR_LITERAL("active") : LOG_STR_LITERAL("cleared"));
223 }
224 this->status_callback_.call(static_cast<DriverEvent>(event), active);
225 }
226 return true;
227}
228
230 ESP_LOGCONFIG(TAG,
231 " Address: %u\n"
232 " IC version: 0x%02X",
233 this->address_, this->version_);
234 // Register reads on a failed driver would only wait for the UART timeout
235 if (!this->is_failed()) {
236 uint8_t irun = this->read_field(IRUN).value_or(0);
237 uint8_t ihold = this->read_field(IHOLD).value_or(0);
238 ESP_LOGCONFIG(TAG, " Microsteps: %u", this->get_microsteps());
239 if (this->analog_current_scale_) {
240 ESP_LOGCONFIG(TAG,
241 " Run current scale: %u\n"
242 " Hold current scale: %u\n"
243 " Current scaled by VREF",
244 irun, ihold);
245 } else {
246 ESP_LOGCONFIG(TAG,
247 " Run current: %.2f A\n"
248 " Hold current: %.2f A\n"
249 " Max current: %.2f A",
250 this->scale_to_current(irun), this->scale_to_current(ihold), this->scale_to_current(31));
251 }
252 }
253 LOG_PIN(" ENN Pin: ", this->enn_pin_);
254 LOG_PIN(" STEP Pin: ", this->step_pin_);
255 LOG_PIN(" DIR Pin: ", this->dir_pin_);
256 LOG_PIN(" INDEX Pin: ", this->index_pin_);
257 LOG_PIN(" DIAG Pin: ", this->diag_pin_);
258 LOG_STEPPER(this);
259 if (this->is_failed()) {
260 ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
261 }
262}
263
265 if (this->index_pin_ != nullptr)
266 this->add_index_pulses_();
267 if (this->diag_store_.triggered) {
268 this->diag_store_.triggered = false;
269 // DIAG rises on a driver error, any other rise is chip specific
270 if (!this->update_status_()) {
271 ESP_LOGW(TAG, "DIAG rose, but the driver status could not be read");
272 } else if ((this->status_ & GSTAT_FLAGS) == 0) {
273 this->on_diag_without_error();
274 }
275 }
276
277 if (this->has_reached_target()) {
278 this->high_freq_.stop();
279 this->moving_ = false;
280 } else {
281 this->high_freq_.start();
282 if (!this->enabled_)
283 this->set_enabled(true);
284 // An unreachable driver is polled only every 10 s. Read it before the first step, so settings lost when the
285 // motor supply dropped are restored before the move runs on the chip defaults.
286 if (!this->moving_ && this->status_poll_skip_ > 0) {
287 this->status_poll_skip_ = this->update_status_() ? 0 : UNREACHABLE_POLL_SKIP;
288 }
289 this->moving_ = true;
290 }
291
292 if (this->index_pin_ != nullptr) {
293 this->loop_serial_();
294 } else {
295 this->loop_step_dir_();
296 }
297}
298
304
306 int32_t remaining = this->target_position - this->current_position;
307 int8_t direction = (remaining > 0) - (remaining < 0);
308 // Ran past the target, restart the ramp from standstill in the other direction
309 if (direction != 0 && direction == -this->direction_)
310 this->current_speed_ = 0.0f;
311 this->calculate_speed_(micros());
312 this->current_speed_ = std::min(this->current_speed_, static_cast<float>(std::abs(remaining)) / APPROACH_TIME_S);
313
314 int32_t vactual = direction * this->speed_to_vactual_(this->current_speed_);
315 int32_t last = this->get_vactual_();
316 if (vactual == last)
317 return;
318 if (vactual != 0 && (vactual > 0) == (last > 0) && std::abs(vactual - last) < std::abs(last) / VACTUAL_CHANGE_DIVISOR)
319 return;
320 // Pulses that arrive while VACTUAL changes direction are counted in the new direction
322 this->direction_ = direction;
323 this->write_field(VACTUAL, static_cast<uint32_t>(vactual));
324}
325
327 int32_t direction = this->should_step_();
328 if (direction == 0)
329 return;
330 if (direction != this->direction_) {
331 this->dir_pin_->digital_write(direction > 0);
332 this->direction_ = direction;
334 }
335 this->step_state_ = !this->step_state_;
336 this->step_pin_->digital_write(this->step_state_);
337}
338
340 if (this->get_vactual_() != 0)
341 this->write_field(VACTUAL, 0);
342}
343
344void TMC22XXStepper::set_enabled(bool enabled) {
345 if (!enabled) {
346 if (this->get_vactual_() != 0)
347 this->write_field(VACTUAL, 0);
348 if (this->index_pin_ != nullptr) {
349 // Count the pulses up to the stop, so the position is current before the target is set
350 this->add_index_pulses_();
351 this->index_store_.direction = 0;
352 this->direction_ = 0;
353 }
354 // Stop at the current position, so the motor does not run off again when re-enabled
355 this->target_position = this->current_position;
356 }
357 if (this->enn_pin_ != nullptr) {
358 this->enn_pin_->digital_write(!enabled);
359 } else {
360 this->write_field(TOFF, enabled ? this->toff_ : 0);
361 }
362 this->enabled_ = enabled;
363}
364
365void TMC22XXStepper::set_toff(uint8_t toff) {
366 this->toff_ = toff;
367 // Without an ENN pin, TOFF=0 is what keeps the outputs disabled
368 if (this->enabled_ || this->enn_pin_ != nullptr)
369 this->write_field(TOFF, toff);
370}
371
372void TMC22XXStepper::set_microsteps(uint16_t microsteps) {
373 for (uint8_t mres = 0; mres <= 8; mres++) {
374 if ((256u >> mres) == microsteps) {
375 this->write_field(MRES, mres);
376 return;
377 }
378 }
379 ESP_LOGW(TAG, "Invalid microsteps %u", microsteps);
380}
381
383 auto mres = this->read_field(MRES);
384 if (!mres.has_value() || *mres > 8)
385 return 0;
386 return 256u >> *mres;
387}
388
390 if (auto scale = this->current_to_scale_(current))
391 this->write_field(IRUN, *scale);
392}
393
395 if (auto scale = this->current_to_scale_(current))
396 this->write_field(IHOLD, *scale);
397}
398
400 return (this->vsense_active_ ? 0.180f : 0.325f) /
401 ((std::isnan(this->rsense_) ? INTERNAL_RSENSE_OHM : this->rsense_) + SENSE_RESISTOR_OFFSET);
402}
403
405 return (std::min<uint8_t>(scale, 31) + 1) / 32.0f * this->full_scale_current_() / std::numbers::sqrt2_v<float>;
406}
407
408optional<uint8_t> TMC22XXStepper::current_to_scale_(float current) {
409 if (this->analog_current_scale_) {
410 ESP_LOGW(TAG, "Current in A is not supported with analog current scale, use irun/ihold");
411 return {};
412 }
413 float scale = 32.0f * std::numbers::sqrt2_v<float> * current / this->full_scale_current_() - 1.0f;
414 if (scale > 31.0f) {
415 ESP_LOGW(TAG, "%.2f A is above the %.2f A limit, using the limit", current, this->scale_to_current(31));
416 return 31;
417 }
418 return static_cast<uint8_t>(std::max(0.0f, std::round(scale)));
419}
420
421int32_t TMC22XXStepper::speed_to_vactual_(float speed) const {
422 // VACTUAL is in microsteps per 2^24 clock cycles
423 return static_cast<int32_t>(std::lround(speed * 16777216.0f / this->clock_frequency_));
424}
425
427 switch (reg) {
428 case REG_IHOLD_IRUN:
429 return &this->ihold_irun_;
430 case REG_TPOWERDOWN:
431 return &this->tpowerdown_;
432 case REG_TPWMTHRS:
433 return &this->tpwmthrs_;
434 case REG_VACTUAL:
435 return &this->vactual_reg_;
436 default:
437 return nullptr;
438 }
439}
440
441bool TMC22XXStepper::write_register(uint8_t reg, uint32_t value) {
442 uint8_t datagram[DATAGRAM_SIZE] = {SYNC_BYTE,
443 this->address_,
444 static_cast<uint8_t>(reg | WRITE_BIT),
445 static_cast<uint8_t>(value >> 24),
446 static_cast<uint8_t>(value >> 16),
447 static_cast<uint8_t>(value >> 8),
448 static_cast<uint8_t>(value),
449 0};
450 datagram[7] = datagram_crc(datagram, 7);
451
452 while (this->available())
453 this->read();
454 this->write_array(datagram, DATAGRAM_SIZE);
455 this->flush();
456 // On the single wire bus every byte sent is also received, drop that echo
457 uint8_t echo[DATAGRAM_SIZE];
458 if (!this->read_array(echo, DATAGRAM_SIZE) || memcmp(echo, datagram, DATAGRAM_SIZE) != 0) {
459 ESP_LOGV(TAG, "No echo writing register 0x%02X", reg);
460 this->status_set_warning(LOG_STR(ESP_LOG_MSG_COMM_FAIL));
461 return false;
462 }
463 this->status_clear_warning();
464
465 if (uint32_t *shadow = this->shadow_register(reg)) {
466 *shadow = value;
467 } else if (int index = restored_index(reg); index >= 0) {
468 this->restored_values_[index] = value;
469 this->restored_written_ |= 1u << index;
470 }
471 return true;
472}
473
474optional<uint32_t> TMC22XXStepper::read_register(uint8_t reg) {
475 if (uint32_t *shadow = this->shadow_register(reg))
476 return *shadow;
477
478 uint8_t request[READ_REQUEST_SIZE] = {SYNC_BYTE, this->address_, reg, 0};
479 request[3] = datagram_crc(request, 3);
480
481 while (this->available())
482 this->read();
483 this->write_array(request, READ_REQUEST_SIZE);
484 this->flush();
485 uint8_t reply[DATAGRAM_SIZE];
486 if (!this->read_array(reply, READ_REQUEST_SIZE) || !this->read_array(reply, DATAGRAM_SIZE)) {
487 ESP_LOGV(TAG, "No reply reading register 0x%02X", reg);
488 this->status_set_warning(LOG_STR(ESP_LOG_MSG_COMM_FAIL));
489 return {};
490 }
491 if (reply[0] != SYNC_BYTE || reply[1] != MASTER_ADDRESS || reply[2] != reg || reply[7] != datagram_crc(reply, 7)) {
492 ESP_LOGV(TAG, "Invalid reply reading register 0x%02X", reg);
493 this->status_set_warning(LOG_STR(ESP_LOG_MSG_COMM_FAIL));
494 return {};
495 }
496 this->status_clear_warning();
497 return encode_uint32(reply[3], reply[4], reply[5], reply[6]);
498}
499
501 // Skip the UART timeout when the driver never answered during setup
502 if (this->is_failed())
503 return false;
504 optional<uint32_t> data;
505 int index = restored_index(field.reg);
506 if (index >= 0 && (this->restored_written_ & (1u << index)) != 0) {
507 // Modify the last written value, not the chip: after a driver reset that the poll has not seen yet, the chip
508 // holds its defaults, and those would replace the values to restore
509 data = this->restored_values_[index];
510 } else {
511 data = this->read_register(field.reg);
512 }
513 if (!data.has_value())
514 return false;
515 uint32_t mask = field.mask();
516 return this->write_register(field.reg, (*data & ~mask) | ((value << field.shift) & mask));
517}
518
519optional<uint32_t> TMC22XXStepper::read_field(const RegisterField &field) {
520 auto data = this->read_register(field.reg);
521 if (!data.has_value())
522 return {};
523 return extract_field(*data, field);
524}
525
527 uint32_t value = (data & field.mask()) >> field.shift;
528 if (field.is_signed && field.width < 32) {
529 uint32_t sign_bit = 1u << (field.width - 1);
530 value = (value ^ sign_bit) - sign_bit;
531 }
532 return value;
533}
534
535} // namespace esphome::tmc22xx
uint8_t status
Definition bl0942.h:8
void mark_failed()
Mark this component as failed.
bool is_failed() const
Definition component.h:274
void set_interval(const char *name, uint32_t interval, std::function< void()> &&f)
Set an interval function with a const char* name.
Definition component.cpp:88
void status_clear_warning()
Definition component.h:291
virtual void setup()=0
virtual void digital_write(bool value)=0
void stop()
Stop running the loop continuously.
Definition helpers.cpp:841
void start()
Start running the loop continuously.
Definition helpers.cpp:835
Helper class to disable interrupts.
Definition helpers.h:2088
void attach_interrupt(void(*func)(T *), T *arg, gpio::InterruptType type) const
Definition gpio.h:29
void calculate_speed_(uint32_t now)
Definition stepper.cpp:9
int32_t speed_to_vactual_(float speed) const
Definition tmc22xx.cpp:421
void apply_config_()
Write the settings from the configuration to the readable registers.
Definition tmc22xx.cpp:157
std::array< uint32_t, RESTORED_REGISTERS.size()> restored_values_
Definition tmc22xx.h:230
bool write_register(uint8_t reg, uint32_t value)
Definition tmc22xx.cpp:441
static uint32_t extract_field(uint32_t data, const RegisterField &field)
Definition tmc22xx.cpp:526
void set_microsteps(uint16_t microsteps)
Definition tmc22xx.cpp:372
void set_toff(uint8_t toff)
Chopper off time (1-15). TOFF=0 is used to disable the outputs, so this is applied on the next enable...
Definition tmc22xx.cpp:365
optional< uint8_t > current_to_scale_(float current)
Definition tmc22xx.cpp:408
virtual uint32_t * shadow_register(uint8_t reg)
Return the cached value of a write-only register, or nullptr when the register is readable.
Definition tmc22xx.cpp:426
virtual void restore_registers()
Write the cached write-only registers and the last written configuration registers to the driver,...
Definition tmc22xx.cpp:146
void set_enabled(bool enabled)
Enable or disable the motor outputs, using the ENN pin when configured and TOFF otherwise.
Definition tmc22xx.cpp:344
optional< uint32_t > read_register(uint8_t reg)
Definition tmc22xx.cpp:474
HighFrequencyLoopRequester high_freq_
Definition tmc22xx.h:252
optional< uint32_t > read_field(const RegisterField &field)
Definition tmc22xx.cpp:519
InternalGPIOPin * diag_pin_
Definition tmc22xx.h:222
void set_hold_current(float current)
Definition tmc22xx.cpp:394
LazyCallbackManager< void(DriverEvent, bool)> status_callback_
Definition tmc22xx.h:223
virtual uint8_t expected_version() const =0
IC version reported in IOIN by the supported chip.
float scale_to_current(uint8_t scale)
Convert a current scale (0-31) to A RMS, without analog current scale.
Definition tmc22xx.cpp:404
InternalGPIOPin * index_pin_
Definition tmc22xx.h:221
bool update_status_()
Read GSTAT and DRV_STATUS and report changes. Returns false when the status could not be read.
Definition tmc22xx.cpp:186
optional< uint8_t > ottrim_
Definition tmc22xx.h:239
void set_run_current(float current)
Set the motor run or hold current in A RMS.
Definition tmc22xx.cpp:389
bool write_field(const RegisterField &field, uint32_t value)
Definition tmc22xx.cpp:500
virtual void on_diag_without_error()
Called when DIAG rises without a driver error.
Definition tmc22xx.h:207
UARTFlushResult flush()
Definition uart.h:51
optional< std::array< uint8_t, N > > read_array()
Definition uart.h:40
void write_array(const uint8_t *data, size_t len)
Definition uart.h:27
FanDirection direction
Definition fan.h:5
int speed
Definition fan.h:3
@ INTERRUPT_RISING_EDGE
Definition gpio_pin.h:52
@ INTERRUPT_ANY_EDGE
Definition gpio_pin.h:54
ESPHOME_LOG_TAG(TAG, "tmc22xx")
DriverEvent
Driver conditions reported to on_status, in the order of their status bits: GSTAT bits 0-2,...
Definition tmc22xx.h:90
@ DRIVER_EVENT_TEMPERATURE_157C
Definition tmc22xx.h:105
@ DRIVER_EVENT_SHORT_TO_GROUND_B
Definition tmc22xx.h:97
@ DRIVER_EVENT_LOW_SIDE_SHORT_A
Definition tmc22xx.h:98
@ DRIVER_EVENT_CHARGE_PUMP_UNDERVOLTAGE
Definition tmc22xx.h:93
@ DRIVER_EVENT_DRIVER_ERROR
Definition tmc22xx.h:92
@ DRIVER_EVENT_TEMPERATURE_120C
Definition tmc22xx.h:102
@ DRIVER_EVENT_SHORT_TO_GROUND_A
Definition tmc22xx.h:96
@ DRIVER_EVENT_OVERTEMPERATURE_PREWARNING
Definition tmc22xx.h:94
@ DRIVER_EVENT_OPEN_LOAD_A
Definition tmc22xx.h:100
@ DRIVER_EVENT_TEMPERATURE_143C
Definition tmc22xx.h:103
@ DRIVER_EVENT_OVERTEMPERATURE
Definition tmc22xx.h:95
@ DRIVER_EVENT_TEMPERATURE_150C
Definition tmc22xx.h:104
@ DRIVER_EVENT_LOW_SIDE_SHORT_B
Definition tmc22xx.h:99
@ DRIVER_EVENT_OPEN_LOAD_B
Definition tmc22xx.h:101
const void size_t len
Definition hal.h:68
void IRAM_ATTR HOT delayMicroseconds(uint32_t us)
Definition hal.cpp:48
uint32_t IRAM_ATTR HOT micros()
Definition hal.cpp:43
constexpr uint32_t encode_uint32(uint8_t byte1, uint8_t byte2, uint8_t byte3, uint8_t byte4)
Encode a 32-bit value given four bytes in most to least significant byte order.
Definition helpers.h:988
uint8_t crc8(const uint8_t *data, uint8_t len, uint8_t crc, uint8_t poly, bool msb_first)
Calculate a CRC-8 checksum of data with size len.
Definition helpers.cpp:68
uint8_t reverse_bits(uint8_t x)
Reverse the order of 8 bits.
Definition helpers.h:1019
static void uint32_t
Set by a rising edge on DIAG, which signals a driver error or, on chips that support it,...
Definition tmc22xx.h:110
static void gpio_intr(DiagPinStore *arg)
Definition tmc22xx.cpp:91
Counts the step pulses the driver reports on INDEX while it runs from VACTUAL.
Definition tmc22xx.h:116
static void gpio_intr(IndexPulseStore *arg)
Definition tmc22xx.cpp:89
A bit field inside one register.
Definition tmc22xx.h:15
constexpr uint32_t mask() const
Definition tmc22xx.h:21
SemaphoreHandle_t lock