使用TMC2209驱动与ESP32无法启动步进电机的问题求助
TMC2209 + ESP32 步进电机无法启动故障排查求助
使用TMC2209驱动器搭配ESP32控制步进电机,电机完全无响应,已反复检查接线但问题未解决,请求帮忙排查故障并指导步进电机启动步骤。
我的代码
#define STEP 38 #define EN 37 #define DIR 36 #define INDEX 35 #define DIAG 21 #define MS2 13 #define MS1 14 #define PDN_RX 11 #define PDN_TX 10 #define TX 43 #define RX 44 uint8_t GCONF[] = {0xA, 0x00, 0x80, 0x00, 0x00, 0x00, 0x65, 0x00}; uint8_t FACTORY_CONF[] = {0xA, 0x00, 0x87, 0x00, 0x00, 0x00, 0x1F, 0x00}; uint8_t IHOLD_IRUN[] = {0xA,0x00,0x90,0x00,0x49,0x49,0x00}; uint8_t TPOWERDOWN[] = {0xA, 0x00, 0x91, 0x00, 0x00, 0x00, 0xFF, 0x00}; uint8_t TPWMTHRS[] = {0xA, 0x00, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00}; uint8_t VACTUAL[] = {0xA, 0x00, 0xA2, 0x00, 0xFF, 0xFF, 0xFF, 0x00}; uint8_t SGTHRS[] = {0xA, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x50, 0x00}; uint8_t COOLCONF[] = {0xA, 0x00, 0xC2, 0x00, 0x00, 0xB0, 0x66, 0x00}; const uart_port_t uart_num = UART_NUM_0; uint8_t CalcCRC (uint8_t * datagram, uint8_t datagramLength) { int i, j; /* Loop Counters */ uint8_t *crc = datagram + (datagramLength - 1); // CRC located in last byte of message uint8_t currentByte; // Byte to be sent *crc = 0; // Initialize CRC to 0 for (i = 0; i < (datagramLength - 1); i++) { // Execute for all bytes of a message currentByte = datagram[i]; // Retrieve a byte to be sent from Array for (j = 0; j < 8; j++) { // Execute for all bits of a byte if ((*crc >> 7) ^ (currentByte & 0x01)) { // update CRC based result of XOR operation *crc = (*crc << 1) ^ 0x07; // Shift CRC to the left and XOR with 0x07 } /*of if*/ else { *crc = (*crc << 1); // Shift CRC to the left } /*of else*/ currentByte = currentByte >> 1; /* Shift byte to the right once */ } /*of for j counter*/ } /*of for datagram length*/ return datagram[datagramLength - 1]; // Return the CRC } /*of CalcCRC*/ void CalcRegistersCrc() { uint8_t CRC = CalcCRC((const char *)GCONF, sizeof(GCONF)); printf("%02X\n", CRC); CRC = CalcCRC((const char *)FACTORY_CONF, sizeof(FACTORY_CONF)); printf("%02X\n", CRC); CRC = CalcCRC((const char *)IHOLD_IRUN, sizeof(IHOLD_IRUN)); printf("%02X\n", CRC); CRC = CalcCRC((const char *)TPOWERDOWN, sizeof(TPOWERDOWN)); printf("%02X\n", CRC); CRC = CalcCRC((const char *)TPWMTHRS, sizeof(TPWMTHRS)); printf("%02X\n", CRC); CRC = CalcCRC((const char *)VACTUAL, sizeof(VACTUAL)); printf("%02X\n", CRC); CRC = CalcCRC((const char *)SGTHRS, sizeof(SGTHRS)); printf("%02X\n", CRC); CRC = CalcCRC((const char *)COOLCONF, sizeof(COOLCONF)); printf("%02X\n", CRC); } void WriteRegisters() { uart_write_bytes(uart_num, GCONF, sizeof(GCONF)); uart_write_bytes(uart_num, FACTORY_CONF, sizeof(FACTORY_CONF)); uart_write_bytes(uart_num, IHOLD_IRUN, sizeof(IHOLD_IRUN)); uart_write_bytes(uart_num, TPOWERDOWN, sizeof(TPOWERDOWN)); uart_write_bytes(uart_num, TPWMTHRS, sizeof(TPWMTHRS)); uart_write_bytes(uart_num, VACTUAL, sizeof(VACTUAL)); uart_write_bytes(uart_num, SGTHRS, sizeof(SGTHRS)); uart_write_bytes(uart_num, COOLCONF, sizeof(COOLCONF)); ESP_LOGI("STEPPER MOTOR", "All registers are written"); } void SetPins() { gpio_set_direction(EN, GPIO_MODE_OUTPUT); gpio_set_direction(MS1, GPIO_MODE_OUTPUT); gpio_set_direction(MS2, GPIO_MODE_OUTPUT); gpio_set_level(EN, 0); gpio_set_level(MS1, 0); gpio_set_level(MS2, 1); } void InitUart() { uart_config_t uart_config = { .baud_rate = 115200, .data_bits = UART_DATA_8_BITS, .parity = UART_PARITY_DISABLE, .stop_bits = UART_STOP_BITS_1, .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, .source_clk = UART_SCLK_APB }; uart_param_config(uart_num, &uart_config); uart_set_pin(uart_num, TX, RX, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); uart_driver_install(uart_num, 2048, 0, 0, NULL, 0); } void ReadData() { ; } void StepperMotorTask(void *pvParameters) { CalcRegistersCrc(); SetPins(); InitUart(); while (1) { WriteRegisters(); vTaskDelay(1000 / portTICK_PERIOD_MS); } }
故障排查要点与修正方案
1. 核心问题:缺少STEP脉冲输出
代码中仅定义了STEP和DIR引脚,但未初始化它们的GPIO方向,也没有输出任何STEP脉冲。TMC2209在默认的STEP/DIR模式下,必须接收连续的STEP脉冲才能驱动电机转动。
2. 串口配置冲突
UART_NUM_0是ESP32的默认调试串口(USB转串口),用来和TMC2209通信会与调试输出(printf/ESP_LOGI)冲突,导致寄存器写入失败。建议改用UART_NUM_1或UART_NUM_2,同时确保:
- ESP32的TX引脚接TMC2209的PDN_RX
- ESP32的RX引脚接TMC2209的PDN_TX
- TMC2209的PDN引脚需拉低(接GND),进入UART通信模式
3. 寄存器CRC处理错误
当前代码仅计算CRC并打印,但未将计算后的CRC值写入寄存器数组的最后一位,导致TMC2209无法识别无效的寄存器指令。需要将计算后的CRC填充到对应寄存器数组的末尾。
4. 寄存器重复写入
WriteRegisters函数在循环中每秒执行一次,这完全没必要,寄存器仅需在初始化时写入一次即可,重复写入会干扰驱动器正常工作。
修正后的代码
#define STEP 38 #define EN 37 #define DIR 36 #define INDEX 35 #define DIAG 21 #define MS2 13 #define MS1 14 #define PDN_RX 11 #define PDN_TX 10 #define TX 43 #define RX 44 // 初始化寄存器时预留CRC位,后续计算填充 uint8_t GCONF[] = {0xA, 0x00, 0x80, 0x00, 0x00, 0x00, 0x65, 0x00}; uint8_t FACTORY_CONF[] = {0xA, 0x00, 0x87, 0x00, 0x00, 0x00, 0x1F, 0x00}; uint8_t IHOLD_IRUN[] = {0xA,0x00,0x90,0x00,0x49,0x49,0x00}; uint8_t TPOWERDOWN[] = {0xA, 0x00, 0x91, 0x00, 0x00, 0x00, 0xFF, 0x00}; uint8_t TPWMTHRS[] = {0xA, 0x00, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00}; uint8_t VACTUAL[] = {0xA, 0x00, 0xA2, 0x00, 0x00, 0x00, 0x00, 0x00}; // 初始速度设为0,按需调整 uint8_t SGTHRS[] = {0xA, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x50, 0x00}; uint8_t COOLCONF[] = {0xA, 0x00, 0xC2, 0x00, 0x00, 0xB0, 0x66, 0x00}; const uart_port_t uart_num = UART_NUM_1; // 改用UART1避免调试冲突 uint8_t CalcCRC (uint8_t * datagram, uint8_t datagramLength) { int i, j; uint8_t *crc = datagram + (datagramLength - 1); uint8_t currentByte; *crc = 0; for (i = 0; i < (datagramLength - 1); i++) { currentByte = datagram[i]; for (j = 0; j < 8; j++) { if ((*crc >> 7) ^ (currentByte & 0x01)) { *crc = (*crc << 1) ^ 0x07; } else { *crc = (*crc << 1); } currentByte = currentByte >> 1; } } return *crc; } void InitRegisters() { // 计算并填充所有寄存器的CRC CalcCRC(GCONF, sizeof(GCONF)); CalcCRC(FACTORY_CONF, sizeof(FACTORY_CONF)); CalcCRC(IHOLD_IRUN, sizeof(IHOLD_IRUN)); CalcCRC(TPOWERDOWN, sizeof(TPOWERDOWN)); CalcCRC(TPWMTHRS, sizeof(TPWMTHRS)); CalcCRC(VACTUAL, sizeof(VACTUAL)); CalcCRC(SGTHRS, sizeof(SGTHRS)); CalcCRC(COOLCONF, sizeof(COOLCONF)); } void WriteRegisters() { uart_write_bytes(uart_num, GCONF, sizeof(GCONF)); vTaskDelay(10 / portTICK_PERIOD_MS); // 给驱动器预留响应时间 uart_write_bytes(uart_num, FACTORY_CONF, sizeof(FACTORY_CONF)); vTaskDelay(10 / portTICK_PERIOD_MS); uart_write_bytes(uart_num, IHOLD_IRUN, sizeof(IHOLD_IRUN)); vTaskDelay(10 / portTICK_PERIOD_MS); uart_write_bytes(uart_num, TPOWERDOWN, sizeof(TPOWERDOWN)); vTaskDelay(10 / portTICK_PERIOD_MS); uart_write_bytes(uart_num, TPWMTHRS, sizeof(TPWMTHRS)); vTaskDelay(10 / portTICK_PERIOD_MS); uart_write_bytes(uart_num, VACTUAL, sizeof(VACTUAL)); vTaskDelay(10 / portTICK_PERIOD_MS); uart_write_bytes(uart_num, SGTHRS, sizeof(SGTHRS)); vTaskDelay(10 / portTICK_PERIOD_MS); uart_write_bytes(uart_num, COOLCONF, sizeof(COOLCONF)); vTaskDelay(10 / portTICK_PERIOD_MS); ESP_LOGI("STEPPER MOTOR", "All registers are written"); } void SetPins() { gpio_set_direction(EN, GPIO_MODE_OUTPUT); gpio_set_direction(MS1, GPIO_MODE_OUTPUT); gpio_set_direction(MS2, GPIO_MODE_OUTPUT); gpio_set_direction(STEP, GPIO_MODE_OUTPUT); // 初始化STEP引脚 gpio_set_direction(DIR, GPIO_MODE_OUTPUT); // 初始化DIR引脚 gpio_set_level(EN, 0); // 低电平使能驱动器 gpio_set_level(MS1, 0); // 设置1/4细分 gpio_set_level(MS2, 1); gpio_set_level(DIR, 0); // 设置电机转动方向 gpio_set_level(STEP, 0); // STEP初始电平 } void InitUart() { uart_config_t uart_config = { .baud_rate = 115200, .data_bits = UART_DATA_8_BITS, .parity = UART_PARITY_DISABLE, .stop_bits = UART_STOP_BITS_1, .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, .source_clk = UART_SCLK_APB }; uart_param_config(uart_num, &uart_config); uart_set_pin(uart_num, TX, RX, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); uart_driver_install(uart_num, 2048, 2048, 0, NULL, 0); // 初始化接收缓冲区 } void StepperMotorTask(void *pvParameters) { InitRegisters(); // 先计算并填充寄存器CRC SetPins(); InitUart(); WriteRegisters(); // 仅初始化时写入一次寄存器 while (1) { // 输出STEP脉冲驱动电机,调整延迟时间可改变转速 gpio_set_level(STEP, 1); vTaskDelay(1 / portTICK_PERIOD_MS); // 脉冲宽度1ms gpio_set_level(STEP, 0); vTaskDelay(1 / portTICK_PERIOD_MS); // 脉冲间隔1ms } }
额外检查项
- 确认驱动器VM引脚电源电压符合电机规格(通常为12-24V),且电源能提供足够电流
- 检查电机绕组接线是否正确,无短路或接反
- 测试DIAG引脚状态,若驱动器报错会输出相应信号
内容的提问来源于stack exchange,提问作者Boris
相关产品推荐
相关产品推荐

