
简介基于STM32F4与TMC5130的步进电机控制工程面向从事嵌入式电机控制、自动化设备开发的工程师与学习者。工程以STM32 Cortex-M4为核心结合TMC5130驱动器实现高精度、低噪声的电机驱动方案覆盖SPI/UART接口配置、电流与速度参数调节、运动控制算法、实时监控及安全保护等关键环节。压缩包共790个文件约23.21MB主要包含C/H源码、HAL库驱动文件、IOC与Cproject工程配置、XML描述以及多份调试日志和说明文档由于携带工程版本管理残留文件内容结构略显琐碎但核心代码完整可从中提取实用的工程框架。目前已有177人学习。通过此工程可快速了解两款芯片的软硬件协同方法借鉴速度规划、位置控制和电流调节的代码实现为毕业设计、竞赛或产品开发提供可直接参考的模板。1. 把 TMC5130 挂在 STM32F4 上电机控制才算真正开始做步进电机控制的人多半有过这样的经历用 A4988 或者 DRV8825 把电机转起来不难但一遇到噪音、发热、失步、堵转就要开始和硬件搏斗。TMC5130 这颗芯片和普通步进驱动最大的不同在于它不只是把 PWM 信号换成电机电流而是把整个运动控制前端都收进来了——你通过 SPI 或者 UART 直接告诉它“走到哪、怎么走”剩下的加减速、电流调节、堵转检测全部在芯片内部完成。STM32F4 在这里的角色就不再是实时生成脉冲而是做一个发号施令的决策者。这篇文章会写清楚一件事用 STM32F4 的 SPI 外设把 TMC5130 驱动起来并让它稳定地跑一个带加减速的位置运动。面向的是已经会点 STM32、但第一次接触 Trinamic 系驱动的工程师。你会看到完整的硬件接线、寄存器初始化序列、运动控制逻辑以及几个不那么容易在数据手册里一眼看明白的参数陷阱。标题里提到的“电机控制”在本文特指步进电机的点位运动控制不涉及无刷电机或者伺服但里面的调试思路是通用的。2. 硬件连线与 SPI 通信先把 TMC5130 的嘴巴喂饱2.1 TMC5130 的接口选择SPI 还是 UARTTMC5130 支持两种串行通信方式SPI 和单线 UART。二者共享相同的寄存器地址空间但电气接口和时序完全不同。选择 SPI 而不是 UART 的理由有三个速度快、时序确定性强、便于调试。SPI 时钟可以跑到 4MHz 以上UART 通常是 1Mbps 左右SPI 的电平是标准的 MOSI/MISO/SCK/CS 四线逻辑分析仪或者示波器直接能抓UART 是单线双向终端电阻和时序要求更琐碎。在 STM32F4 上SPI 外设是现成的直接映射到硬件引脚不需要额外模拟。通用做法是SPI2 或 SPI1 都行只要确认引脚没有和调试口冲突。常见选择是 SPI1SCK 在 PA5、MOSI 在 PA7、MISO 在 PA6、CS 随便挑一个 GPIO。2.2 STM32F4 与 TMC5130 的引脚连接表STM32F4 引脚功能接 TMC5130 引脚说明PA5SPI1_SCKSCKSPI 时钟建议 1-4MHz别贪快PA7SPI1_MOSISDI主发从收PA6SPI1_MISOSDO主收从发PA4GPIO 输出CSN片选低电平有效PB0GPIO 输出ENN使能低电平有效PB1GPIO 输出DIR方向仅在 SPI 模式下需要PB2GPIO 输出STEP脉冲输入仅在 SPI 模式下需要3.3V电源VCC_IO逻辑电源TMC5130 的 IO 电平是 3.3V12V/24V电源VM电机电源具体看电机额定电压接线有一个容易踩的坑TMC5130 的 VCC_IO 决定 SPI 引脚的逻辑电平阈值必须和 STM32F4 的供电一致。如果 STM32F4 是 3.3V 供电VCC_IO 就接 3.3V千万别把 VCC_IO 接到 5V 再把 STM32F4 的引脚直接连过去会烧 GPIO。方向引脚和 STEP 引脚在 SPI 模式下不是必需的——通过 SPI 写寄存器就能控制方向和步进——但建议还是预留出来后面调试的时候可以切回脉冲模式做对比这个技巧在后面章节会用到。2.3 SPI 帧格式40 位里藏着地址和数据TMC5130 的 SPI 帧是固定的 40 位8 位地址加上 32 位数据。地址字节的高位是读/写标志最高位bit 7为 0 表示写操作为 1 表示读操作剩余 7 位是寄存器地址。读操作比较特殊主机发送 40 位的读请求帧芯片会在当前帧的 MISO 上回传上一个寄存器地址的内容真正的读结果要到下一次通信才能拿到。这意味着读操作需要连续发两帧第一次触发读、第二次取数据。很多人第一次调试时在这个地方卡住看到 MISO 上采到的全是 0 或者乱数以为自己接线接错了其实只是少发了一帧取数指令。STM32F4 的 SPI 外设配置成主模式数据帧大小设为 8 位MSB 先行CPOL0、CPHA1。TMC5130 要求 SCK 空闲时为低、第二个边沿采样这和很多 SPI 从机不一样。如果 CPOL/CPHA 配反了寄存器读出来全是 0xFFFFFFFF但写操作可能偶尔成功因为写操作对时序的容忍度稍高。建议初始化的时候先写一个读操作来验证时序比如读 ID 寄存器或者 VERSION 寄存器能读到预期的值再往下走。下面这段代码是标准外设库风格的 SPI 读写函数可以直接抄进工程里。#define TMC5130_CS_LOW() GPIO_ResetBits(GPIOA, GPIO_Pin_4) #define TMC5130_CS_HIGH() GPIO_SetBits(GPIOA, GPIO_Pin_4) uint32_t tmc5130_readwrite(uint8_t addr, uint32_t data, uint8_t is_read) { uint8_t addr_field; uint8_t tx_buf[5]; uint8_t rx_buf[5]; uint32_t reply 0; int i; // 地址字段最高位是读标志0写1读 addr_field (is_read ? 0x80 : 0x00) | (addr 0x7F); tx_buf[0] addr_field; tx_buf[1] (data 24) 0xFF; tx_buf[2] (data 16) 0xFF; tx_buf[3] (data 8) 0xFF; tx_buf[4] data 0xFF; TMC5130_CS_LOW(); for (i 0; i 5; i) { while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) RESET); SPI_I2S_SendData(SPI1, tx_buf[i]); while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) RESET); rx_buf[i] SPI_I2S_ReceiveData(SPI1); } TMC5130_CS_HIGH(); // 组装回读数据 for (i 1; i 5; i) { reply (reply 8) | rx_buf[i]; } return reply; }逻辑说明CS 拉低之后连续收发 5 个字节第一个字节是地址后四个是数据。TMC5130 在每个字节的传输过程中都会在 MISO 上回传数据回传的内容是芯片上一次收到的地址对应的寄存器值。组装回读数据时跳过了 rx_buf[0]因为那个字节携带的是地址信息不是有效数据。这个函数同时支持读和写读操作时 data 参数填 0 即可写操作时 is_read 填 0。注意 CS 必须在整个 40 位传输期间保持低电平中间不能拉高否则芯片会认为通信终止帧被丢弃。2.4 初始化 STM32F4 的 SPI 外设SPI 引脚的复用功能要配置对GPIO 模式设为 AF_PP速度设为 50MHz。时钟分频用 SPI_BaudRatePrescaler_16在 84MHz 的 APB2 时钟下得到 5.25MHz 的 SCK略微超出了 TMC5130 的标称上限。稳妥的做法是分频 32得到约 2.6MHz足够用了。把速度提上去对通信延迟的改善微乎其微因为 TMC5130 的运动控制是芯片内部自己完成的不需要频繁读写寄存器。只有在使用 StallGuard2 做闭环堵转检测、需要高频读取状态的时候才会考虑把 SPI 时钟往上提但这需要仔细验证 PCB 走线质量和信号完整性。void tmc5130_spi_init(void) { GPIO_InitTypeDef gpio; SPI_InitTypeDef spi; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE); // SCK: PA5, MOSI: PA7, MISO: PA6 复用为 SPI1 gpio.GPIO_Pin GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7; gpio.GPIO_Mode GPIO_Mode_AF; gpio.GPIO_OType GPIO_OType_PP; gpio.GPIO_PuPd GPIO_PuPd_NOPULL; gpio.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, gpio); // CS: PA4 推挽输出默认拉高 gpio.GPIO_Pin GPIO_Pin_4; gpio.GPIO_Mode GPIO_Mode_OUT; gpio.GPIO_OType GPIO_OType_PP; gpio.GPIO_PuPd GPIO_PuPd_NOPULL; GPIO_Init(GPIOA, gpio); GPIO_SetBits(GPIOA, GPIO_Pin_4); SPI_I2S_DeInit(SPI1); SPI_InitTypeDef spi_init; spi_init.SPI_Direction SPI_Direction_2Lines_FullDuplex; spi_init.SPI_Mode SPI_Mode_Master; spi_init.SPI_DataSize SPI_DataSize_8b; spi_init.SPI_CPOL SPI_CPOL_Low; spi_init.SPI_CPHA SPI_CPHA_2Edge; spi_init.SPI_NSS SPI_NSS_Soft; spi_init.SPI_BaudRatePrescaler SPI_BaudRatePrescaler_32; spi_init.SPI_FirstBit SPI_FirstBit_MSB; SPI_Init(SPI1, spi_init); SPI_Cmd(SPI1, ENABLE); }提示SPI_NSS 必须设置为软件管理也就是 SPI_NSS_Soft否则硬件会自动控制片选和手动拉 CS 冲突。这里把 NSS 引脚释放出来当作普通 GPIO 用片选的拉低拉高完全由软件控制时序上更灵活。硬件上还有一个容易忽略的点TMC5130 的 CLK 引脚需要外部时钟输入。可以用一个有源晶振也可以直接复用 STM32F4 的 MCO 引脚输出时钟。MCO1 可以输出 HSE 时钟比如 8MHz连接到 TMC5130 的 CLK 引脚。如果这块没接好芯片上电后寄存器写操作表现为正常但电机不会动因为内部运动控制器的时钟源不存在。检查方法是用示波器量 CLK 引脚应该有连续的方波。3. 寄存器配置从静音模式到电流环一次把参数写对3.1 核心寄存器一览GCONF、IHOLD_IRUN、CHOPCONF、PWMCONFTMC5130 的寄存器有几十个但真正初始化驱动必须写的只有四类。GCONF 控制全局功能包括是否启用内部电流调节器、是否开启 StealthChop 静音模式、SPI 通信模式下的地址选择等。IHOLD_IRUN 设置电机静止保持电流和运行电流以百分比表示百分比的基准是 VREF 引脚的电压或者内部参考电压。CHOPCONF 是斩波器配置决定电流调节的工作方式包括 SpreadCycle 和 StealthChop 的选择、斩波频率、电流衰减时间等。PWMCONF 在 StealthChop 模式下生效控制 PWM 的频率、幅度和自动调谐行为。寄存器名称地址关键位段典型值说明GCONF0x00pdn_disable1, mstep_reg1, stealth10x00000004pdn_disable 必须置 1否则 UART 引脚被占用IHOLD_IRUN0x10IHOLD8, IRUN16, IHOLDDELAY50x00080F08运行电流为 16%保持电流 8%CHOPCONF0x6Ctoff3, hstrt5, hend0, tbl2, vsense10x000101D3StealthChop 模式下的推荐基础配置PWMCONF0x70PWM_AMPL180, PWM_FREQ10x000401B4自动调谐开启PWM 频率约 28kHzTCOOLTHRS0x14低速阈值低于此速度启用 StealthChop0x000186A0数值 100000 对应约 120rpmGCONF 里的 pdn_disable 位和 DIAG 引脚冲突。TMC5130 的 DIAG 引脚在 UART 模式下用作单向数据线在 SPI 模式下如果不把 pdn_disable 置 1芯片会认为 DIAG 引脚上的电平变化是在切换 UART 模式导致 SPI 通信异常。这个位的名字叫 pdn_disable全称是 power-down disable置 1 后禁止进入掉电模式同时也禁用了 UART 引脚的复用。写 GCONF 时一定要把这个位置 1这是 Trinamic 芯片在 SPI 模式下最常见的初始化遗漏。IHOLD_IRUN 的电流百分比计算方式是 current_percent / 32 * VREF 电压 / 内部感应电阻。32 是电流调节器的最大档位8 表示 8/32 25% 的满量程电流。实际电流值还受 CHOPCONF 里 vsense 位的影响vsense0 时满量程电流约为 1.77A RMSvsense1 时约为当前值的一半。做原型验证的时候建议 vsense1降低电流感应电阻上的功耗等确认电机力矩需求后再改回 0。IRUN 的数值要根据实际电机额定电流反推先保守设到 16约 50%跑起来后用温升和噪音做调整。3.2 StealthChop 与 SpreadCycle 的取舍静音不是免费的StealthChop 是 Trinamic 的静音斩波技术通过电压 PWM 调制和自动调谐让电机在低速段几乎听不到声音。SpreadCycle 是传统的电流滞环斩波方式噪音大但力矩平滑度和高速特性更好。TMC5130 允许通过 TCOOLTHRS 和 THIGH 两个阈值设定速度区间低于 TCOOLTHRS 时用 StealthChop高于 THIGH 时强制切到 SpreadCycle中间区域自动过渡。实际使用中中高速段用 SpreadCycle 的噪声已经不明显所以默认配置通常把 THIGH 设为最大值让芯片自己决定何时切换。void tmc5130_init_config(void) { // GCONF: pdn_disable1, mstep_reg1, stealthchop1 tmc5130_write(0x00, 0x00000004); // IHOLD_IRUN: 运行电流 50%, 保持电流 25% tmc5130_write(0x10, 0x00080F10); // CHOPCONF: toff3, hstrt5, hend2, tbl2, vsense1 tmc5130_write(0x6C, 0x00010133); // PWMCONF: 自动调谐, PWM 频率 28kHz, 幅度 180 tmc5130_write(0x70, 0x000401B4); // TCOOLTHRS: 低速阈值 100000 tmc5130_write(0x14, 0x000186A0); // 使能电机驱动 tmc5130_write(0x02, 0x00000001); // DRVCTRL 的 enable 位 }逻辑分析写 GCONF 时同时把 stealth 位和 mstep_reg 位置 1前者启用 StealthChop后者允许通过 SPI 寄存器修改微步数。CHOPCONF 里 toff 设为 3 是驱动电流衰减时间的粗调参数太小会让电流纹波变大、电机发热增加太大会导致电流无法在高速时跟上指令。hstrt5、hend2 组合是数据手册推荐的起始点实际效果因电机而异后面调噪音主要就是调这两个值。PWMCONF 里 PWM_AMPL 的初始值 180 是自动调谐的起点芯片会根据电机反电动势自动调整到最优值。写寄存器之后的验证方法是把芯片内部的 VACTUAL 寄存器读回来。VACTUAL 记录当前的实际位置单位是微步。让电机空转几圈后断开电源重新上电重新初始化后读 VACTUAL如果和断电前一致说明 SPI 通信和寄存器写入全链路正常。不一致则需要回头检查上一步的接线和时序。3.3 GSTAT 寄存器第一个要读的状态寄存器GSTAT 记录了芯片的复位原因和错误标志包括上电复位、欠压复位和驱动错误。上电后第一次读 GSTAT 如果读到非零值需要用写 1 清 0 的方式复位标志位否则后续错误检测会失效。GSTAT 的地址是 0x01读操作返回一个 32 位值只有最低三位有意义。驱动错误位bit 2对应的硬件原因是过温、过流或者对地短路一旦置位芯片会关闭电机驱动器需要复位或重新写寄存器才能恢复。排错时如果电机完全不动第一步应该读 GSTAT 而不是检查电流。如果 bit 2 为 1说明驱动级已经自我保护了常见原因是电机线短路或者 IRUN 设得过大导致过温。如果 GSTAT 只有 bit 0 的复位标志那问题出在使能位或者 STEP/DIR 信号上。这个习惯能省掉很多盲目的硬件排查时间。4. 运动控制与闭环梯形加减速和 StallGuard2 失步检测4.1 位置运动直接写 XTARGET 就能走TMC5130 的运动控制器使用相对坐标和绝对坐标两种模式。RAMPMODE 寄存器设为 0 时执行定位到绝对位置模式芯片会自动按照内部加速度设定走到 XTARGET 指定的位置。加速度由 RAMPMODE 为 0 时的 ACCEL1 和 VMAX 寄存器决定单位是微步每秒平方和微步每秒。这些参数不需要 STM32F4 参与芯片内部的运动控制器会实时计算当前位置和速度并在到达目标后自动停止同时保持电流切回 IHOLD 值。void tmc5130_move_abs(uint32_t target_pos, uint32_t speed, uint32_t accel) { // 设置最大速度单位微步/秒 tmc5130_write(0x2B, speed); // VMAX // 设置加速度单位微步/秒² tmc5130_write(0x2C, accel); // ACCEL1 // 设置目标位置 tmc5130_write(0x2D, target_pos); // XTARGET }参数说明VMAX 是运行速度上限实际速度还受加速度和当前位置与目标距离的限制。ACCEL1 是加速阶段的斜率减速阶段的斜率由 DMAX 寄存器单独控制默认和 ACCEL1 相等。如果目标位置和当前位置离得很近芯片会自动调整速度曲线不会出现先加速到 VMAX 再急刹的情况。XTARGET 是 32 位有符号数范围正负 2,147,483,647 微步配合 256 微步转一圈的配置够绝大多数应用跑几个月不溢出。调用这个函数后STM32F4 就解放了。可以去做其他事情——读传感器、更新显示屏、处理通信协议——不需要再管电机的运动状态。如果中途需要改变目标或者急停重新写 XTARGET 就会被芯片接收运动控制器会重新规划路径。急停则把 VMAX 临时设成 0芯片会在下一个控制周期里停下来等效于立即停止。4.2 StallGuard2 堵转检测不用编码器也能知道失步StallGuard2 是 Trinamic 的专利技术通过检测电机反电动势估算负载力矩不需要额外的编码器或霍尔传感器。负载越大SG_VALUE 读数值越小当 SG_VALUE 降到 SGTHRS 设定的阈值之下时芯片会在 DIAG 引脚上输出一个脉冲同时把 RAMP_STAT 寄存器的 stall 位置 1。实际使用中有两个场景一是做堵转保护电机卡住时立即停机二是做参考点搜索把机械限位当作零位信号省掉极限开关。使用 StallGuard2 有两个前置条件驱动必须工作在 SpreadCycle 模式且电机速度要足够高。StealthChop 模式下无法使用 StallGuard2因为电压 PWM 调制的方式无法精确反推负载力矩。TCOOLTHRS 设定一个较高的阈值让电机在运行速度下自动切到 SpreadCycle同时把 SGTHRS 设为一个合适的值。SGTHRS 的初始设定没有捷径只能在实机上测试让电机正常空转读 SG_VALUE记下数值再增大负载直到失步临界读此时的 SG_VALUE把阈值设在两者之间的中位。uint8_t tmc5130_check_stall(void) { uint32_t ramp_stat tmc5130_read(0x35); // RAMP_STAT 寄存器 if (ramp_stat 0x00000002) { // bit 1: stallGuard2 触发 tmc5130_write(0x2D, 0x00000000); // 急停回到原点 return 1; } return 0; }这段代码放在主循环里轮询即可。RAMP_STAT 的 bit 1 是 stall 标志位写任意值到 RAMP_STAT 寄存器可以将它清零。实测中发现堵转检测存在一定的误报率尤其是在负载波动大或者机械系统有共振的情况下。如果出现误报可以先用示波器看 DIAG 引脚的电平——正常工作时空闲时必须为高堵转时会跳变为低并保持一段时间。把 SGTHRS 的值向减小方向调整变灵敏每次调整 50 左右观察误报是否消失。4.3 闭环的建议位置闭环做在 STM32F4 侧还是 TMC5130 内部TMC5130 内部有一个基于 StallGuard2 的闭环版本称为 StallGuard2 闭环模式可以自动调节电流以保持恒定力矩储备。但实际用下来这个模式对机械系统的刚度要求较高调试参数也比较繁琐多数项目还是倾向于把位置闭环放在 STM32F4 侧读 VACTUAL 或者外部编码器用 PID 控制器输出目标位置然后写 XTARGET 让 TMC5130 执行。这种方案的优越性在于 STM32F4 可以同时处理其他任务比如视觉定位和 IO 控制而 TMC5130 只做一个高效的运动执行器。具体的 PID 参数和调节方法在电机控制场景里已经有很多成熟的实践这里不再展开。5. 三个必调的实战参数与验证方法5.1 电流环的纹波与噪音调试如果你听到电机在低速时发出高频啸叫问题几乎都出在 CHOPCONF 的 toff、hstrt、hend 三个参数上。三者的作用分别是调节电流衰减时间、斩波起始时间和斩波结束偏移量。起始配置用的是数据手册的通用推荐值但不同电机因为电感量差异显著最优配置完全不同。调试步骤是从 toff3 开始保持 hstrt5 和 hend2 不变把 toff 依次调到 4、5、6每次听两分钟运行噪音找到啸叫最轻的值。然后固定 toff调 hstrt 从 3 到 7步进为 1找到噪音拐点。一般 6 次以内就能找到该电机的最优组合。5.2 用逻辑分析仪验证 SPI 时序SPI 通信出问题时最直接的手段是用逻辑分析仪抓取实际波形。设置采样率 10MHz 以上触发条件设为 CS 下降沿抓取一帧完整的 40 位通信。重点检查三处CS 拉低期间 SCK 是否有丢脉冲MISO 上的数据是否在 SCK 的第二个边沿稳定建立以及帧与帧之间 CS 的高电平时间是否足够长。TMC5130 要求相邻两帧之间的间隔至少 1 微秒如果 STM32F4 的代码里在 CS 拉高之后立即开始下一次传输这个间隔无法保证偶发读写失败就会不时出现。解决方法是每次 CS 拉高后加一个 1 微秒的延时。5.3 电机不动时的最小排查序列顺着下面这个顺序排查比盲目改参数有效得多。上电读 GSTAT如果 bit 2 被置位先解决过流或短路问题然后读 VACTUAL 并写入一个小的 XTARGET比如 1000 个微步看 VACTUAL 是否跟着变如果 VACTUAL 变化但电机不动检查 ENN 引脚电平是否为低电机电源 VM 是否到位如果 VACTUAL 不变重点查 CLK 引脚有没有时钟输入。最后一步是用示波器量电机线圈两端的电压正常运动时应该有频率随速度变化的正弦波如果测量不到说明驱动级没有输出问题必然出在芯片的硬件配置而非软件。本文还有配套的精品资源点击获取