拓冰建站拓冰建站
首页 / 资讯中心 / 正文

STM32+TB6600步进电机控制:定时器PWM脉冲生成与加减速实战

简介本资源是一套面向嵌入式初学者与STM32进阶开发者的实战型电机控制案例聚焦于使用STM32微控制器驱动TB6600芯片精确控制42/57系列步进电机解决转速调节、步数计数与角度定位等核心问题适用于智能硬件、机电一体化及自动化控制类项目开发。压缩包共172个文件含50个头文件.h定义外设接口与电机逻辑23个C源码.c实现TIM中断配置、PWM生成、方向/使能/脉冲信号时序控制及细分模式切换另有编译输出文件.axf/.hex、工程配置.uvprojx/.ioc、备份文件.bak及调试日志.log整体大小为7.08MB。已有10783人学习下载资源结构完整包含可直接编译运行的Keil工程配套dian_ji.c/h等模块化驱动代码、main.c主控逻辑及my_lib封装库便于理解TIM定时器中断触发步进、PWM占空比调速、GPIO控制TB6600细分档位等关键技术点是掌握嵌入式电机闭环控制的优质实践素材。 前段时间帮朋友改一台桌面小铣床原来用A4988驱动42步进电机负载一重就丢步干脆整套换成TB6600驱动板电机也换成了57系列。驱动板本身好接麻烦的是控制端TB6600要的是脉冲方向信号而stm32这边怎么生成稳定不抖动的脉冲、怎么在TIM中断里统计步数、怎么配合PWM做加减速前前后后绕了好几天才全部跑通。这个案例特别适合正在用stm32驱动TB6600控制42/57步进电机的朋友不管是做3D打印机、小型雕刻机还是简单的运动控制平台控制逻辑基本是通用的。我把整个调试过程、方案取舍和踩过的坑都整理出来代码也给了标准库和HAL库两个版本照着做能少走不少弯路。1. 为什么是定时器PWM TB6600组合方案选型的实际考量1.1 三种脉冲生成方式哪种最适合步进电机用过步进电机的人都知道驱动器需要的信号说白了就是脉冲串——一个脉冲走一步或一个细分步。生成脉冲串的方式有几种最常见的三种纯延时翻转IO、定时器中断翻转IO、定时器PWM硬件输出。纯延时翻转IO也就是GPIO_HIGH; delay(); GPIO_LOW; delay();这种写法最简单的实验没问题但有一个致命问题延时函数本身受中断影响一旦有串口中断、ADC中断插进来脉冲间隔就被拉长步进电机的转速会突然变化严重时直接丢步。而且这种写法占死CPU电机转起来之后你什么都干不了。定时器中断翻转IO比纯延时好一些频率稳定不占主循环但中断频率等于脉冲频率。比如你要输出10kHz脉冲就得在中断里翻转一次IOCPU每个周期都在跑中断服务函数负载不小。更麻烦的是如果脉冲频率太高比如20kHz以上中断里还要处理加减速、方向切换等逻辑很容易出问题。定时器PWM硬件输出就不一样了。PWM的周期和占空比完全由定时器硬件生成不占CPU资源频率精确到微秒级不受中断延迟影响。脉冲频率等于PWM频率脉冲宽度就是PWM的高电平时间。对步进电机来说PWM输出就是天然的脉冲源。这才是实用的方案。所以我最终选了定时器PWM输出脉冲 通用IO控制方向和使能的方案。1.2 TB6600在驱动链路上到底做了什么TB6600是市面上最常见的步进电机驱动板之一标称电流最高4A实际长时间用建议打八折适配42和57系列的步进电机。它内置了功率MOSFET和电流斩波控制逻辑输入只需要三路信号脉冲PUL、方向DIR、使能ENA输出直接接管电机的两相四线。它的内部原理简单说就是每来一个脉冲内部计数器走一个细分步然后根据当前位置和电流目标值通过H桥输出对应的电压/电流波形。所以你能看到脉冲频率决定转速脉冲数量决定转动角度DIR电平决定方向。这对控制端非常友好——你不需要关心电机绕组的换相时序那些全被TB6600内部处理掉了。这也是为什么TB6600比直接用MOSFET搭H桥要省心得多。TB6600还有一个很实用的特性输入信号经过光耦隔离。这意味着单片机和驱动板之间没有电气上的直接连接外部电源的干扰不容易窜进单片机。但对于新手来说这个光耦隔离也是一把双刃剑——接线接不对脉冲发不出来电机一动不动后面我会专门说这个坑。1.3 定时器PWM和TIM中断分别承担什么角色这套方案里PWM和TIM中断的分工要理清楚PWM用于产生脉冲频率转速越快PWM频率越高。假设电机转一圈需要1600个脉冲8细分那PWM频率1600Hz时转速就是60rpmPWM频率3200Hz时就是120rpm。这个关系在整个项目里是最核心的换算逻辑。TIM中断用于数脉冲和变速度虽然PWM是硬件生成的但单片机得知道已经发了多少个脉冲接下来该加速还是减速。最简单的做法是让PWM定时器的更新中断去计步每产生一个PWM周期就进一次中断变量加1。中断频率不高时几kHz以内完全没问题。频率更高时再换成主从模式的外部计数方案后面细说。一句话总结PWM负责稳定输出TIM中断负责感知和决策两者配合才能做出一套可控的步进电机运动控制。2. 硬件接线与拨码开关设置照着接也要知道为什么2.1 引脚接线PUL/DIR/ENA的正确接法TB6600驱动板的接线端子很多但其实分三组电源输入、电机输出、控制信号输入。先看控制信号这组也是最容易接错的地方。TB6600的PUL、DIR、ENA各有两个端子PUL和PUL-、DIR和DIR-、ENA和ENA-。每个信号都是一个光耦隔离输入本质就是一个发光二极管加限流电阻。所以接线本质上是把发光二极管点亮——需要电流流过而不是简单地给个高电平就可以了。最常见的接法是共阳接法把PUL、DIR、ENA全部接到同一个正电源上5V或3.3V都行然后PUL-接单片机脉冲引脚DIR-接方向引脚ENA-接使能引脚。这种接法下单片机引脚输出低电平时光耦回路导通信号有效。另一种接法是共阴接法所有负端接地脉冲信号的端接单片机引脚。这种接法要求单片机引脚输出高电平来驱动光耦对引脚驱动能力要求高一些。我实际用下来3.3V的STM32驱动TB6600是完全可行的但有几个细节要注意PUL接3.3V时光耦限流电阻一般按5V设计3.3V供电时电流会小一些但TB6600光耦的驱动电流要求不高一般是7-15mA大部分板子3.3V也能可靠触发。保险起见可以量一下PUL和PUL-之间的电压如果光耦导通时电压差小于1.2V说明光耦没导通需要换接法。如果单片机引脚是3.3V电平建议把正端接3.3V而不是5V。虽然大多数TB6600输入的限流电阻能承受5V灌入但直接用3.3V更稳妥避免电平不匹配。有些新款TB6600支持高电平有效/低电平有效切换具体看板子丝印或者说明书不确定的用万用表量一下端子两端电压。另外ENA信号如果不接默认是使能状态电机可以正常转。但注意有些板子的ENA是低电平使能有些是高电平使能这个差异经常让人反复折腾。电机绕组的四根线接A、A-、B、B-顺序错了会导致电机反转如果只接了一相绕组电机会发出高频啸叫但不转。电机线颜色对照表一般是42电机常见红色A、绿色A-、蓝色B、黄色B-57电机则五花八门一定要以电机标签为准。2.2 拨码开关电流和细分设置的对照表TB6600板子上的拨码开关通常有8个SW1到SW8。前三个SW1-SW3设置电流中间一个SW4设置自动半流后四个SW5-SW8设置细分数。不同品牌丝印不一样但逻辑基本一致以板子上的丝印为准。电流设置这边要特别用心电流太大电机会发热电流太小扭矩不够容易丢步。42电机建议设置1.5A左右57电机建议2.5A到3A。注意拨码不精确是几个固定档位选一个最接近目标值的即可。SW1SW2SW3峰值电流(A)ONONON0.5OFFONON1.0ONOFFON1.5OFFOFFON2.0ONONOFF2.5OFFONOFF3.0ONOFFOFF3.5OFFOFFOFF4.0细分设置决定转一圈需要多少脉冲。常规档位有1、2、4、8、16、32细分。以1.8°步距角的42/57电机为例SW5SW6SW7SW8细分数每转脉冲数ONONONON1整步200OFFONONON2400ONOFFONON4800OFFOFFONON81600ONONOFFON163200OFFONOFFON326400细分数怎么选低速高扭矩场合比如雕刻机Z轴用4或8细分就够了高速轻载场合比如3D打印机用16细分比较顺滑。细分越高步距角越小振动越小但相同转速下需要的脉冲频率越高。对STM32来说PWM输出几千赫兹毫无压力所以细分数对单片机这边影响不大主要是驱动器和电机发热的取舍。2.3 电源搭配与共接地线的注意事项TB6600的电源输入端子通常标VM和VM-接直流电源42电机推荐24V/3A以上57电机推荐36V或更高电压/5A以上。电源电压越高电机高速性能越好但驱动板发热也越大。注意电源必须电容滤波充分劣质开关电源在电机启停瞬间电压跌落严重会直接导致丢步。说到接地这里有一个很多新手踩过的坑。TB6600的控制信号输入是光耦隔离理论上输入侧和输出侧可以不共地。但实际调试时单片机的地GND最好还是和电源的地连在一起否则悬空的地会让光耦输入侧电压不确定脉冲信号可能时有时无。正确的做法是开关电源的负极同时接到TB6600的VM-和STM32的GND保证逻辑参考点一致。还有一个容易被忽略的点电机启动时的电流冲击很大电源线和电机线尽量短而粗不要和信号线捆在一起。我之前用杜邦线连接走线一长脉冲信号被干扰电机偶尔抖动后来换成屏蔽线并远离电机线才解决。3. 定时器PWM配置频率计算、初始化代码和中断计数方案3.1 PWM频率怎么算从转速目标反推ARR和PSC用STM32的定时器生成PWM频率公式是PWM频率 定时器时钟频率 / ((PSC 1) * (ARR 1))以STM32F103为例定时器时钟是72MHz。如果我们想得到1Hz到1MHz范围可调的PWM常用做法是固定PSC让ARR决定频率。比如PSC设置为71即72000000 / 72 1MHz此时PWM频率 1000000 / (ARR 1)那ARR和转速怎么换算举个例子驱动器设8细分电机每转需要1600个脉冲。想让电机跑60rpm每秒1圈每秒需要1600个脉冲即PWM频率1600Hz。代入公式1600 1000000 / (ARR 1) ARR 1000000 / 1600 - 1 624简单吧。反过来给定PWM频率求转速转速(rpm) PWM频率(Hz) * 60 / 每转脉冲数比如PWM频率2000Hz8细分1600脉冲/转转速 2000 * 60 / 1600 75 rpm我在代码里直接用一个宏定义保存每转脉冲数然后通过一个函数把目标转速换算成ARR值传入定时器这样后续调参只需要改宏不需要翻代码。实际调试时最方便的方式是在串口中断里接收转速命令换算成ARR后调用PWM设置函数。3.2 HAL库和标准库的PWM初始化代码先看HAL库版本。以TIM3_CH1PA6输出PWM为例TIM_HandleTypeDef htim3; TIM_OC_InitTypeDef sConfigOC; void MX_TIM3_PWM_Init(uint16_t arr, uint16_t psc) { __HAL_RCC_TIM3_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); GPIO_InitTypeDef GPIO_InitStruct {0}; GPIO_InitStruct.Pin GPIO_PIN_6; GPIO_InitStruct.Mode GPIO_MODE_AF_PP; GPIO_InitStruct.Speed GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOA, GPIO_InitStruct); htim3.Instance TIM3; htim3.Init.Prescaler psc - 1; htim3.Init.CounterMode TIM_COUNTERMODE_UP; htim3.Init.Period arr - 1; htim3.Init.ClockDivision TIM_CLOCKDIVISION_DIV1; htim3.Init.AutoReloadPreload TIM_AUTORELOAD_PRELOAD_DISABLE; HAL_TIM_PWM_Init(htim3); sConfigOC.OCMode TIM_OCMODE_PWM1; sConfigOC.Pulse arr / 2; // 50%占空比 sConfigOC.OCPolarity TIM_OCPOLARITY_HIGH; sConfigOC.OCFastMode TIM_OCFAST_DISABLE; HAL_TIM_PWM_ConfigChannel(htim3, sConfigOC, TIM_CHANNEL_1); HAL_TIM_PWM_Start(htim3, TIM_CHANNEL_1); } // 设置输出频率arr 1000000 / target_freq - 1 void PWM_Set_Freq(uint32_t target_freq) { uint16_t arr 1000000 / target_freq - 1; __HAL_TIM_SET_AUTORELOAD(htim3, arr - 1); __HAL_TIM_SET_COMPARE(htim3, TIM_CHANNEL_1, arr / 2); }标准库版本和控制逻辑其实差不多void TIM3_PWM_Init(u16 arr, u16 psc) { GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitStructure.GPIO_Pin GPIO_Pin_6; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, GPIO_InitStructure); TIM_TimeBaseStructure.TIM_Period arr - 1; TIM_TimeBaseStructure.TIM_Prescaler psc - 1; TIM_TimeBaseStructure.TIM_ClockDivision 0; TIM_TimeBaseStructure.TIM_CounterMode TIM_CounterMode_Up; TIM_TimeBaseInit(TIM3, TIM_TimeBaseStructure); TIM_OCInitStructure.TIM_OCMode TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse arr / 2; TIM_OCInitStructure.TIM_OCPolarity TIM_OCPolarity_High; TIM_OC1Init(TIM3, TIM_OCInitStructure); TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable); TIM_ARRPreloadConfig(TIM3, ENABLE); TIM_Cmd(TIM3, ENABLE); }几个注意点占空比设为50%就够了。步进电机驱动器只关心脉冲的上升沿或下降沿脉冲宽度不是太重要。但不要设得太小比如占总周期1%可能因为光耦响应延迟导致触发不稳定。50%最省心。AutoReloadPreload最好设为DISABLE这样修改ARR会立即生效方便动态调速。如果设为ENABLE新ARR要等下一个更新事件才生效加减速时会产生一个周期的延迟问题不大但调试时容易摸不着头脑。初始化完先不要启动PWM等方向、使能信号就绪后再HAL_TIM_PWM_Start避免上电瞬间电机误动。3.3 脉冲计数更新中断方案和主从定时器方案电机转了多少度本质上就是数了多少个脉冲。需要一个机制来统计PWM脉冲个数。最简单的方案在PWM定时器上开启更新中断每产生一个PWM周期就进一次更新中断用一个变量计数。volatile uint32_t pulse_count 0; volatile uint32_t target_count 0; volatile uint8_t motion_done 0; void TIM3_IRQHandler(void) { if (__HAL_TIM_GET_FLAG(htim3, TIM_FLAG_UPDATE)) { __HAL_TIM_CLEAR_FLAG(htim3, TIM_FLAG_UPDATE); pulse_count; if (pulse_count target_count) { HAL_TIM_PWM_Stop(htim3, TIM_CHANNEL_1); motion_done 1; } } }这个方案思路直接但有一个限制更新中断的频率等于PWM频率。如果PWM频率是10kHz那么中断每秒触发1万次每次执行不到1微秒实际测下来STM32F103完全撑得住。但如果你把细分设到32转速又拉满PWM频率可能到100kHz那中断每10微秒一次还要在中断里做计数、比较、停机判断会占用不少CPU时间此时就要换方案了。换什么方案利用STM32的主从定时器功能主定时器输出PWM从定时器用主定时器的TRGO信号作为外部时钟进行计数。这样脉冲计数完全由硬件完成不占中断。当从定时器溢出时产生中断或者直接读取计数器值判断是否走完。以TIM3输出PWM、TIM2作为外部计数器为例void MX_TIM2_ExtCounter_Init(void) { __HAL_RCC_TIM2_CLK_ENABLE(); TIM_SlaveConfigTypeDef sSlaveConfig {0}; sSlaveConfig.SlaveMode TIM_SLAVEMODE_EXTERNAL1; sSlaveConfig.InputTrigger TIM_TS_ITR2; // TIM3的TRGO映射到TIM2的ITR2 sSlaveConfig.TriggerPolarity TIM_INPUTCHANNELPOLARITY_RISING; sSlaveConfig.TriggerFilter 0; HAL_TIM_SlaveConfigSynchro(htim2, sSlaveConfig); } // TIM3配置为主模式TRGO输出更新事件 void MX_TIM3_MasterConfig(void) { TIM_MasterConfigTypeDef sMasterConfig {0}; sMasterConfig.MasterOutputTrigger TIM_TRGO_UPDATE; sMasterConfig.MasterSlaveMode TIM_MASTERSLAVEMODE_ENABLE; HAL_TIMEx_MasterConfigSynchronization(htim3, sMasterConfig); }这样TIM2的计数器值就是已发出的脉冲数清零后重新计时即可。不过这个方案配置起来复杂度高不少如果只是做实验或者控制频率不高的场合更新中断的计数方案完全够用。我先用更新中断方案跑通了整个功能后面才换成了主从计数分了两步走避免一次引入太多变量。4. 加减速与方向控制的软件实现4.1 为什么步进电机需要加减速丢步的根源步进电机有一个物理特性转子有一定的转动惯量起步瞬间如果脉冲频率太高转子来不及跟随定子磁场的变化就会拖步甚至丢步。这就是为什么很多实验一上来电机就开始咔咔咔乱响而不转——指令频率超过电机能承受的启动频率。解决办法就是加减速。电机从低速开始逐渐加速到目标速度到目标位置前逐渐减速到低速再停止。这样电机全程都在可同步的范围内不会丢步。加减速曲线有很多种梯形、S形、指数形。最简单的梯形加减速在大多数场合已经够用加速一段、匀速一段、减速一段。S形曲线更平滑适合对振动和精度要求高的场合。对入门项目来说梯形加减速完全够用代码量少调参直观。4.2 简易梯形加减速在中断里动态修改ARR实现梯形加减速核心思想是在PWM运行过程中动态修改ARR值来改变频率。频率从低逐步升高就是加速从高逐步降低就是减速。我在中断里维护了一个速度档位索引每个档位对应一个ARR值。加速时每N个脉冲升一档减速时反过来降一档。代码结构大致如下#define MAX_SPEED_STEP 20 // 加速表从慢到快的ARR值 const uint16_t speed_tbl[MAX_SPEED_STEP] { 1000, 900, 820, 750, 690, 640, 600, 565, 535, 510, 488, 468, 450, 435, 420, 408, 396, 385, 375, 366 }; volatile uint16_t speed_index 0; volatile uint16_t step_in_this_speed 0; void TIM3_IRQHandler(void) { if (__HAL_TIM_GET_FLAG(htim3, TIM_FLAG_UPDATE)) { __HAL_TIM_CLEAR_FLAG(htim3, TIM_FLAG_UPDATE); pulse_count; if (accel_state ACCELERATING) { step_in_this_speed; if (step_in_this_speed 50 speed_index MAX_SPEED_STEP - 1) { speed_index; step_in_this_speed 0; __HAL_TIM_SET_AUTORELOAD(htim3, speed_tbl[speed_index] - 1); __HAL_TIM_SET_COMPARE(htim3, TIM_CHANNEL_1, speed_tbl[speed_index] / 2); } } else if (accel_state DECELERATING) { // 逻辑类似只是 speed_index 递减 } if (pulse_count target_count) { HAL_TIM_PWM_Stop(htim3, TIM_CHANNEL_1); motion_done 1; } } }这里的speed_tbl是我预先算好的加速表每个元素对应一个ARR值。加速表怎么生成可以写个小脚本按起始频率和目标频率指数分布生成也可以手工算。我实际项目中用了一个简单的转换公式把目标转速rpm转成ARR值uint16_t rpm_to_arr(uint16_t rpm, uint16_t pulses_per_rev) { // 1MHz计数频率下 return 60000000UL / (rpm * pulses_per_rev) - 1; }注意修改ARR时如果开了ARR预装载实际生效要等当前周期结束如果没开立即生效但可能产生一个不完整周期。步进电机对单个周期长度不敏感所以直接用__HAL_TIM_SET_AUTORELOAD修改即可占空比同时改一下保持50%。调加减速参数时有个经验加速每档维持的脉冲数不要取得太小。比如从500Hz加速到10kHz每档只维持几个脉冲相当于接近直接跳频照样会丢步。通常每档维持50到200个脉冲比较稳。如果你的电机惯性大、负载重可以适当增加每档脉冲数让加速更缓精度要求高的场合档位数量也要增多每档变化量减小。4.3 方向控制、使能和限位信号的配合方向控制很直接在启动PWM之前先设置DIR引脚电平。但要注意方向切换需要一定的时间让驱动器电流建立所以DIR切换之后要延时几毫秒再发脉冲。我习惯在切换方向前停了PWM然后设置DIR电平再延时5ms最后启动PWM。void motor_move(int16_t steps, uint8_t direction) { if (steps 0) return; HAL_GPIO_WritePin(DIR_GPIO_Port, DIR_Pin, direction); delay_ms(5); pulse_count 0; target_count steps 0 ? steps : -steps; accel_state ACCELERATING; speed_index 0; HAL_TIM_PWM_Start(htim3, TIM_CHANNEL_1); }使能信号ENA一般只在需要释放电机轴时使用比如手动调零、换料正常运动过程中保持使能状态。注意不同TB6600的ENA逻辑不一致有的高电平使能有的低电平使能按板子丝印来接用万用表量一下引脚电压就能确定。如果你要做限位保护比如雕刻机Z轴归零限位开关信号应该接到STM32的外部中断引脚。限位触发时外部中断里直接停PWM、清方向标志同时设置一个错误标记主循环再处理后续逻辑。不要在主循环里轮询限位响应太慢高速运动时可能已经撞上了。5. 实测调试记录踩过的坑和排查过程5.1 有PWM波形但电机不转这是我整个调试过程中遇到最多的问题是用逻辑分析仪或者示波器能看到单片机引脚有PWM波形但电机不转。症状很明确但原因有好几种得按顺序排查。第一步查ENABLE信号。很多TB6600的使能逻辑是低电平使能如果你的ENA引脚悬空或电平不对驱动器处于脱机状态绕组没有电流电机可以自由转动断电状态。用手拧一下电机轴如果能轻松转动说明使能没生效。第二步查光耦电流。前面说过PUL接3.3V或5VPUL-接单片机引脚是常见的接法。但如果PUL接5V而STM32引脚开到推挽输出低电平此时光耦限流电阻上的压降差不多5V-1.2V电流大约7mA以上通常能触发。但如果你接反了——PUL接单片机IOPUL-接GND那么STM32引脚高电平只有3.3V减去光耦压降1.2V限流电阻如果是470Ω电流约4.5mA也能触发但临界一些。所以接法不对光耦工作在临界导通状态时灵时不灵。第三步查脉冲频率和占空比。脉冲频率太高比如超出驱动器响应范围或占空比太小比如1%都会导致驱动芯片检测不到有效脉冲。把频率降到几百赫兹、占空比调到50%再试。我的排查顺序建议是先看ENA状态再看PUL接线和电流最后看PWM参数。这几个顺序不能反因为你如果先调参数可能调半天发现是使能问题。5.2 电机抖动、共振或噪音大的原因电机能转但抖得厉害或者尖叫声很大也是常见问题。抖动最常见的原因是脉冲频率和电机固有频率接近。步进电机在低速段通常几百Hz有低频共振整步或半步模式下特别明显。解决办法有三个方向一是提高细分数比如从整步改到8细分或16细分让步距更小共振能量摊薄二是提高电流让电机刚性更足三是尽量避免在那个转速区间停留。高频啸叫通常来自驱动器电流斩波频率和电机的相互作用只要不是特别离谱一般属于正常现象。改善方法给驱动板和电机做减振缓冲或者调整电流档位找到噪音和扭矩的平衡点。另外电机抖动还要查一下电机线有没有接触不良。我用快速接线端子连接57电机线时端子没有完全压紧导致一相断路电机抖动且没有扭矩查了好久才发现。这种故障属于间歇性接触万用表通断测试都查不出来最后用替换线材才定位到。5.3 运行中单片机复位或卡死这个坑比较隐蔽。现象是电机一转STM32就复位程序从头跑或者不定时卡死串口无响应。第一个嫌疑是电源。电机启动时电流突增如果电源容量不足电压被拉低STM32的3.3V稳压器也会跟着掉电直接复位。解决方法是加大电源容量或在电源输入端并一个大电容1000uF以上电解电容。电机驱动电源和单片机电源最好不要共用一个模块我用的是双电源方案24V/5A供给TB66005V/2A经过AMS1117给STM32供电两个电源的地线在TB6600的VM-处单点相连。第二个嫌疑是脉冲信号线上的干扰。电机线里的电流很大切换瞬间产生强烈的电磁干扰如果信号线和电机线走在一起干扰会灌入PUL引脚导致单片机误触发。解决方法是信号线用双绞线或屏蔽线远离电机线或者在信号线上串一个100Ω到220Ω的电阻降低干扰强度。第三个嫌疑是程序逻辑问题。更新中断里如果写了耗时操作比如printf串口打印、HAL_Delay中断频率一高主循环就被卡死。有一种非常典型的情况在中断里调用HAL_Delay而HAL_Delay依赖SysTick中断如果SysTick优先级和当前中断优先级相同或更低HAL_Delay永远等不到SysTick程序直接卡死。这个坑我见过不止一次。中断服务函数里只做标志位、计数、寄存器操作所有打印和延时全部放到主循环处理。5.4 一组实测参数记录我用57步进电机步距角1.8°额定电流3A配合TB6600做了几组实测参数和现象整理如下供参考。电机电压24V驱动板电流设置3A细分81600脉冲/转脉冲频率(Hz)理论转速(rpm)现象2007.5低速平稳轻微振动50018.75正常噪声明显160060稳定运行无共振3200120正常扭矩有所下降5000187.5运行正常但电机发热明显8000300轻微丢步需要加电启动或提高电压这组数据说明24V下1600脉冲/转的细分设置转速在60到120rpm之间最稳妥。如果追求更高转速电源电压要提高到36V甚至48V同时每转脉冲数可以降低调低细分但会牺牲步距精度。关于起始频率我测下来57电机空载直接从1600Hz启动没问题但带负载后直接从500Hz以上启动就会丢步。所以实际项目里我把起始频率固定在200Hz左右加速每档100Hz左右每档持续100个脉冲这样负载适应性最好。5.5 一个容易被忽视的细节JTAG引脚冲突最后聊一个容易浪费一整天的问题。如果你用的引脚恰好是JTAG调试口的复用引脚比如PA13、PA14、PA15、PB3、PB4而你在初始化代码里把他们配置成了普通GPIO结果就是程序下载一次之后第二次就下载不进去了因为调试口被占用了。我习惯用TIM3_CH1也就是PA6做PWM输出虽然不涉及JTAG但方向引脚我一开始用了PB4JTAG NJTRST折腾了一整天才反应过来。如果用标准库要在GPIO初始化前调用GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);禁用JTAG引脚HAL库里则是先配置AFIO再调用__HAL_AFIO_REMAP_SWJ_NOJTAG();。或者更简单直接避开这些引脚用PA、PB的普通IO做方向控制。最后再分享一个调试技巧如果你手头有逻辑分析仪一定要学会看PWM波形。我在调试加减速的时候每次修改完参数第一件事就是抓一下PWM波形看频率变化是否平滑、有没有突变。波形上每段频率对应一个台阶如果台阶太大就会出现加速过猛导致丢步如果整个波形非常平滑说明参数比较合理。这一步比反复让电机空转试错高效得多。这套stm32 TB6600 TIM中断 PWM的控制方案做好之后扩展性很强加减速表可以扩展成S形曲线脉冲计数可以换成主从定时器方案方向控制可以加上限位和原点信号再往上就是完整的运动控制轴了。我后来在这套代码基础上做了一个三轴的小型运动平台控制逻辑基本没怎么变只是把每个轴的参数表独立开了。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门