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;
20static constexpr float INTERNAL_RSENSE_OHM = 0.17f;
23static constexpr float APPROACH_TIME_S = 0.02f;
25static constexpr int32_t VACTUAL_CHANGE_DIVISOR = 16;
26static constexpr uint32_t STATUS_POLL_INTERVAL_MS = 1000;
29static constexpr uint8_t UNREACHABLE_POLL_SKIP = 9;
30static constexpr uint32_t GSTAT_FLAGS = 0x07;
31static constexpr uint32_t DRV_STATUS_FLAGS = 0x0FFF;
33static const LogString *driver_event_to_string(
DriverEvent 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");
66 return LOG_STR(
"Unknown");
71static uint8_t datagram_crc(
const uint8_t *data,
size_t len) {
73 for (
size_t i = 0; i <
len; i++) {
75 crc =
crc8(&reflected, 1, crc, 0x07,
true);
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);
100 if (!version.has_value()) {
101 ESP_LOGE(TAG,
"No response from driver at address %u", this->
address_);
107 ESP_LOGW(TAG,
"Unexpected IC version 0x%02X", this->
version_);
151 for (
size_t i = 0; i < RESTORED_REGISTERS.size(); i++) {
170 if (toff.has_value() && *toff != 0)
188 if (!gstat.has_value())
194 if (!value.has_value())
199 if ((*gstat & GSTAT_FLAGS) != 0)
202 uint16_t
status = (*gstat & GSTAT_FLAGS) | ((drv_status & DRV_STATUS_FLAGS) << 3);
205 ESP_LOGW(TAG,
"Restoring settings after driver reset");
214 if ((changed & (1u << event)) == 0)
216 bool active = (
status & (1u << event)) != 0;
219 ESP_LOGW(TAG,
"%s", LOG_STR_ARG(driver_event_to_string(
static_cast<DriverEvent>(event))));
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"));
232 " IC version: 0x%02X",
236 uint8_t irun = this->
read_field(IRUN).value_or(0);
237 uint8_t ihold = this->
read_field(IHOLD).value_or(0);
241 " Run current scale: %u\n"
242 " Hold current scale: %u\n"
243 " Current scaled by VREF",
247 " Run current: %.2f A\n"
248 " Hold current: %.2f A\n"
249 " Max current: %.2f A",
253 LOG_PIN(
" ENN Pin: ", this->
enn_pin_);
255 LOG_PIN(
" DIR Pin: ", this->
dir_pin_);
260 ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
271 ESP_LOGW(TAG,
"DIAG rose, but the driver status could not be read");
272 }
else if ((this->
status_ & GSTAT_FLAGS) == 0) {
307 int8_t
direction = (remaining > 0) - (remaining < 0);
318 if (vactual != 0 && (vactual > 0) == (last > 0) && std::abs(vactual - last) < std::abs(last) / VACTUAL_CHANGE_DIVISOR)
373 for (uint8_t mres = 0; mres <= 8; mres++) {
374 if ((256u >> mres) == microsteps) {
379 ESP_LOGW(TAG,
"Invalid microsteps %u", microsteps);
384 if (!mres.has_value() || *mres > 8)
386 return 256u >> *mres;
401 ((std::isnan(this->
rsense_) ? INTERNAL_RSENSE_OHM : this->
rsense_) + SENSE_RESISTOR_OFFSET);
405 return (std::min<uint8_t>(scale, 31) + 1) / 32.0f * this->
full_scale_current_() / std::numbers::sqrt2_v<float>;
410 ESP_LOGW(TAG,
"Current in A is not supported with analog current scale, use irun/ihold");
413 float scale = 32.0f * std::numbers::sqrt2_v<float> * current / this->
full_scale_current_() - 1.0f;
415 ESP_LOGW(TAG,
"%.2f A is above the %.2f A limit, using the limit", current, this->
scale_to_current(31));
418 return static_cast<uint8_t
>(std::max(0.0f, std::round(scale)));
442 uint8_t datagram[DATAGRAM_SIZE] = {SYNC_BYTE,
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),
450 datagram[7] = datagram_crc(datagram, 7);
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);
467 }
else if (
int index = restored_index(reg); index >= 0) {
478 uint8_t request[READ_REQUEST_SIZE] = {SYNC_BYTE, this->
address_, reg, 0};
479 request[3] = datagram_crc(request, 3);
485 uint8_t reply[DATAGRAM_SIZE];
487 ESP_LOGV(TAG,
"No reply reading register 0x%02X", reg);
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);
497 return encode_uint32(reply[3], reply[4], reply[5], reply[6]);
504 optional<uint32_t> data;
505 int index = restored_index(field.
reg);
513 if (!data.has_value())
521 if (!data.has_value())
530 value = (value ^ sign_bit) - sign_bit;
void mark_failed()
Mark this component as failed.
void status_set_warning()
void set_interval(const char *name, uint32_t interval, std::function< void()> &&f)
Set an interval function with a const char* name.
void status_clear_warning()
virtual void digital_write(bool value)=0
void stop()
Stop running the loop continuously.
void start()
Start running the loop continuously.
Helper class to disable interrupts.
void attach_interrupt(void(*func)(T *), T *arg, gpio::InterruptType type) const
bool has_reached_target()
void calculate_speed_(uint32_t now)
int32_t speed_to_vactual_(float speed) const
uint8_t status_poll_skip_
float initial_run_current_
void apply_config_()
Write the settings from the configuration to the readable registers.
void on_shutdown() override
std::array< uint32_t, RESTORED_REGISTERS.size()> restored_values_
bool write_register(uint8_t reg, uint32_t value)
static uint32_t extract_field(uint32_t data, const RegisterField &field)
int32_t get_vactual_() const
void set_microsteps(uint16_t microsteps)
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...
optional< uint8_t > current_to_scale_(float current)
float initial_hold_current_
virtual uint32_t * shadow_register(uint8_t reg)
Return the cached value of a write-only register, or nullptr when the register is readable.
virtual void restore_registers()
Write the cached write-only registers and the last written configuration registers to the driver,...
uint32_t clock_frequency_
void set_enabled(bool enabled)
Enable or disable the motor outputs, using the ENN pin when configured and TOFF otherwise.
uint16_t get_microsteps()
optional< uint32_t > read_register(uint8_t reg)
uint16_t initial_microsteps_
HighFrequencyLoopRequester high_freq_
optional< uint32_t > read_field(const RegisterField &field)
IndexPulseStore index_store_
InternalGPIOPin * diag_pin_
void set_hold_current(float current)
float full_scale_current_()
LazyCallbackManager< void(DriverEvent, bool)> status_callback_
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.
void dump_config() override
InternalGPIOPin * index_pin_
uint8_t restored_written_
bool analog_current_scale_
bool update_status_()
Read GSTAT and DRV_STATUS and report changes. Returns false when the status could not be read.
optional< uint8_t > ottrim_
void set_run_current(float current)
Set the motor run or hold current in A RMS.
bool write_field(const RegisterField &field, uint32_t value)
virtual void on_diag_without_error()
Called when DIAG rises without a driver error.
optional< std::array< uint8_t, N > > read_array()
void write_array(const uint8_t *data, size_t len)
ESPHOME_LOG_TAG(TAG, "tmc22xx")
DriverEvent
Driver conditions reported to on_status, in the order of their status bits: GSTAT bits 0-2,...
@ DRIVER_EVENT_TEMPERATURE_157C
@ DRIVER_EVENT_SHORT_TO_GROUND_B
@ DRIVER_EVENT_LOW_SIDE_SHORT_A
@ DRIVER_EVENT_CHARGE_PUMP_UNDERVOLTAGE
@ DRIVER_EVENT_DRIVER_ERROR
@ DRIVER_EVENT_TEMPERATURE_120C
@ DRIVER_EVENT_SHORT_TO_GROUND_A
@ DRIVER_EVENT_OVERTEMPERATURE_PREWARNING
@ DRIVER_EVENT_OPEN_LOAD_A
@ DRIVER_EVENT_TEMPERATURE_143C
@ DRIVER_EVENT_OVERTEMPERATURE
@ DRIVER_EVENT_TEMPERATURE_150C
@ DRIVER_EVENT_LOW_SIDE_SHORT_B
@ DRIVER_EVENT_OPEN_LOAD_B
void IRAM_ATTR HOT delayMicroseconds(uint32_t us)
uint32_t IRAM_ATTR HOT micros()
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.
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.
uint8_t reverse_bits(uint8_t x)
Reverse the order of 8 bits.
Set by a rising edge on DIAG, which signals a driver error or, on chips that support it,...
static void gpio_intr(DiagPinStore *arg)
Counts the step pulses the driver reports on INDEX while it runs from VACTUAL.
static void gpio_intr(IndexPulseStore *arg)
volatile int8_t direction
A bit field inside one register.
constexpr uint32_t mask() const