DMA 原理从 0 到 1:为什么它能解放 CPU?一文讲透 DMA 的工作流程
【福利三:雨露均沾·逢7狂欢】之五:STM32系统时钟PLL故障排除1例
【福利三:雨露均沾·逢7狂欢】之三:AI帮我查BUG
福利三:雨露均沾·逢7狂欢】之二:STM32F407VG串口通信乱码故障的排除
基于STM32F103的I2C主从机通信
OpenBLT移植到STM32F103战舰开发板上适用于所有STM32F103系列的Bootloader
TFT LCD 与 FSMC 的硬件连接大容量 STM32F10xxx FSMC 接口
单片机:初学者该了解的STM32F103基础知识
2025软件工具兔哥知道
STM32之继电器模块
微信公众号
手机版
/**- J' ` M# o3 Y& x# [
* 函数功能: 定时器中断服务函数9 u* V$ Z$ q* T& [& M
* 输入参数: 无9 d. X' e7 q! I* V \# B
* 返 回 值: 无
* 说 明: 实现加减速过程
*/, L4 V) o9 }+ ^/ O& ^
void STEPMOTOR_TIMx_IRQHandler(void)//定时器中断处理
{
__IO uint16_t tim_count=0;
// 保存新(下)一个延时周期$ T' o/ I: t8 A3 h% S/ l, p, I
uint16_t new_step_delay=0;
// 加速过程中最后一次延时(脉冲周期).. Z$ O; G5 S0 L) C$ c
__IO static uint16_t last_accel_delay=0;8 |8 [4 c4 Q9 x- @7 ~- l( R# L: Z
// 总移动步数计数器% c3 G) ^" V* m
__IO static uint32_t step_count = 0;
// 记录new_step_delay中的余数,提高下一步计算的精度
__IO static int32_t rest = 0;3 K! j8 c3 a3 S
//定时器使用翻转模式,需要进入两次中断才输出一个完整脉冲9 c7 M! s( _! T2 \7 a) L! I
__IO static uint8_t i=0;
if(__HAL_TIM_GET_IT_SOURCE(&htimx_STEPMOTOR, STEPMOTOR_TIM_IT_CCx) !=RESET)0 W! L5 M4 C! X% ?& p$ G
{
// 清楚定时器中断8 B! E; F) Y! K P* U. J' R! O" X
__HAL_TIM_CLEAR_IT(&htimx_STEPMOTOR, STEPMOTOR_TIM_IT_CCx);
; |2 M& Y# b4 H2 ~
// 设置比较值
tim_count=__HAL_TIM_GET_COUNTER(&htimx_STEPMOTOR);7 |" P. a4 X! ^5 k! i6 S
__HAL_TIM_SET_COMPARE(&htimx_STEPMOTOR,STEPMOTOR_TIM_CHANNEL_x,tim_count+srd.step_delay);
2 ]7 c1 l" R3 j
i++; // 定时器中断次数计数值4 U5 P3 r5 R2 v2 I5 ^5 M, Q
if(i==2) // 2次,说明已经输出一个完整脉冲
{
i=0; // 清零定时器中断次数计数值/ H* H9 Y8 I# s3 T0 P- ?- }/ g
switch(srd.run_state) // 加减速曲线阶段0 Q- A; Y. w, ?7 }6 n/ d5 e
{
case STOP:
step_count = 0; // 清零步数计数器
rest = 0; // 清零余值$ L3 X( h) T1 ^: @) S! f
// 关闭通道9 v- q$ V: M8 n5 x
TIM_CCxChannelCmd(STEPMOTOR_TIMx, STEPMOTOR_TIM_CHANNEL_x, TIM_CCx_DISABLE); 7 ]& q- a0 @* \6 y& Y7 Z
__HAL_TIM_CLEAR_FLAG(&htimx_STEPMOTOR, STEPMOTOR_TIM_FLAG_CCx);
STEPMOTOR_OUTPUT_DISABLE();
MotionStatus = 0; // 电机为停止状态
break;( v( z' F. }1 M; T. o
0 }7 m2 K9 i6 G5 J7 o
case ACCEL:
step_count++; // 步数加1# T3 {' j" T, w
if(srd.dir==CW); Q- s" P. X. F3 ~* D9 V7 R
{
step_position++; // 绝对位置加1( r9 A* [2 f$ T. K) l
}0 ~$ z5 q" ]! p$ K- G
else
{9 S* L* W9 H& P1 O: t( x. K- }
step_position--; // 绝对位置减1: p. ?! N. i5 ~9 }4 F
}
srd.accel_count++; // 加速计数值加16 Y5 L$ V0 n3 |5 p* s
new_step_delay = srd.step_delay - (((2 *srd.step_delay) + rest)/(4 * srd.accel_count + 1));//计算新(下)一步脉冲周期(时间间隔)0 c1 r3 O( N1 ?! ?8 z
rest = ((2 * srd.step_delay)+rest)%(4 * srd.accel_count + 1);// 计算余数,下次计算补上余数,减少误差& E) X7 R" a% Q7 G' u; {3 x; m0 {
if(step_count >= srd.decel_start)// 检查是够应该开始减速
{# J$ g1 S8 [ X. S* j5 O
srd.accel_count = srd.decel_val; // 加速计数值为减速阶段计数值的初始值; u3 s. I$ H, {7 p6 _! ]+ i
srd.run_state = DECEL; // 下个脉冲进入减速阶段& L4 i( G) d6 v' h8 o3 ^" V/ D
}. x* k d9 o" a8 g6 S
else if(new_step_delay <= srd.min_delay) // 检查是否到达期望的最大速度, Z3 e! I$ o/ u7 q: K
{
last_accel_delay = new_step_delay; // 保存加速过程中最后一次延时(脉冲周期)/ w; C2 \' G2 s6 q, j6 L; _( F
new_step_delay = srd.min_delay; // 使用min_delay(对应最大速度speed)
rest = 0; // 清零余值7 f* }+ U3 Y, a) v+ Z
srd.run_state = RUN; // 设置为匀速运行状态. |" L& i$ @+ S$ t6 v
}
break;* [ O2 x; e- |/ T; l
3 I% w0 B. V4 ~3 N
case RUN:
step_count++; // 步数加1* |; x) U% O s4 u$ e
if(srd.dir==CW)
{
step_position++; // 绝对位置加1
}
else( u5 u& C' k6 X; M
{
step_position--; // 绝对位置减1
}
new_step_delay = srd.min_delay; // 使用min_delay(对应最大速度speed)
if(step_count >= srd.decel_start) // 需要开始减速
{- d9 T9 _+ A+ ]: W* }. }% w
srd.accel_count = srd.decel_val; // 减速步数做为加速计数值
new_step_delay = last_accel_delay;// 加阶段最后的延时做为减速阶段的起始延时(脉冲周期)$ n$ F* _& c: e: d5 r' t
srd.run_state = DECEL; // 状态改变为减速
}
break;3 j5 B# r% W6 [! y9 S2 z
case DECEL:
step_count++; // 步数加1
if(srd.dir==CW)* b1 i. r$ ]; c' \& t U
{ 4 W) d+ ~; @9 o( ^* i
step_position++; // 绝对位置加1
}; J- p' m! ~& n; I8 Y" d4 c
else
{
step_position--; // 绝对位置减1
}2 g2 \; l5 ^- `( {2 g6 p
srd.accel_count++;$ x y& i9 }& ^
new_step_delay = srd.step_delay - (((2 * srd.step_delay) + rest)/(4 * srd.accel_count + 1)); //计算新(下)一步脉冲周期(时间间隔)* b" {8 s! ?( y; n- [0 r
rest = ((2 * srd.step_delay)+rest)%(4 * srd.accel_count + 1);// 计算余数,下次计算补上余数,减少误差( U8 p; f% Q* Z3 I! j
9 Z" m: O6 ^8 _8 G0 O
//检查是否为最后一步
if(srd.accel_count >= 0)2 H k( T n& C& j9 e% r7 d6 t
{; z9 o8 y7 C4 r0 |7 g$ `
srd.run_state = STOP;
}/ |3 G0 x' q' I& @5 D7 p) @+ _
break;
} ( |' ]8 P8 |/ v' |) q0 b/ V) g, O1 g0 a
srd.step_delay = new_step_delay; // 为下个(新的)延时(脉冲周期)赋值
}6 | p' b, j' {/ Y2 k. \8 I" U* s. v
}
}& p- ^! n6 q9 F/ c7 S* c+ b- D
/**
* 函数功能: 相对位置运动:运动给定的步数* N W, p7 d- }
* 输入参数: step:移动的步数 (正数为顺时针,负数为逆时针).& U. C! X8 k+ Z3 o e9 _
accel 加速度,实际值为accel*0.1*rad/sec^2
decel 减速度,实际值为decel*0.1*rad/sec^2 x9 l' ?( t% \' w
speed 最大速度,实际值为speed*0.1*rad/sec
* 返 回 值: 无
* 说 明: 以给定的步数移动步进电机,先加速到最大速度,然后在合适位置开始
* 减速至停止,使得整个运动距离为指定的步数。如果加减速阶段很短并且( X+ s8 S, v+ K2 a8 b+ b
* 速度很慢,那还没达到最大速度就要开始减速
*/& R& J M+ G8 _! v! ?
void STEPMOTOR_AxisMoveRel(__IO int32_t step, __IO uint32_t accel, __IO uint32_t decel, __IO uint32_t speed), d# Z* i. }( O8 D
{
__IO uint16_t tim_count;! j% s2 y0 n3 T I& d4 o- H2 w$ X
// 达到最大速度时的步数
__IO uint32_t max_s_lim;
// 必须要开始减速的步数(如果加速没有达到最大速度)/ J) F; y# \% ~2 k3 Q7 |/ Q
__IO uint32_t accel_lim;
if(step < 0) // 步数为负数
{: ]2 i+ x2 |& z' L5 Z2 h
srd.dir = CCW; // 逆时针方向旋转8 D, m) R! [: q% M5 D* b
STEPMOTOR_DIR_REVERSAL();
step =-step; // 获取步数绝对值
}
else3 {3 d' j9 s3 ?. z
{1 W7 i! g; _5 F
srd.dir = CW; // 顺时针方向旋转
STEPMOTOR_DIR_FORWARD();
}$ \3 P+ s6 \! F, ]
' q5 W% D1 N5 N9 c
if(step == 1) // 步数为1) [% ^* a& h5 u: ^8 h* j
{3 ^. ?2 Q' R# [8 [4 K* T
srd.accel_count = -1; // 只移动一步/ R# ]8 t& r6 D3 ?2 A
srd.run_state = DECEL; // 减速状态.- s( p2 u9 e! B# c; V3 a3 A
srd.step_delay = 1000; // 短延时 8 @* G( z2 l6 }5 k7 |
}
else if(step != 0) // 如果目标运动步数不为0
{
// 我们的驱动器用户手册有详细的计算及推导过程
: q, ^% w! E% ]- }& z8 K o, ]
// 设置最大速度极限, 计算得到min_delay用于定时器的计数器的值。
// min_delay = (alpha / tt)/ w
srd.min_delay = (int32_t)(A_T_x10/speed);
// 通过计算第一个(c0) 的步进延时来设定加速度,其中accel单位为0.1rad/sec^2' C% e, T4 o- @& R3 e% S+ H
// step_delay = 1/tt * sqrt(2*alpha/accel)* r( [, ~$ r3 v4 j1 y4 @
// step_delay = ( tfreq*0.676/10 )*10 * sqrt( (2*alpha*100000) / (accel*10) )/1001 t7 H4 j4 w( J% r
srd.step_delay = (int32_t)((T1_FREQ_148 * sqrt(A_SQ / accel))/10);
2 Y/ G5 S# M7 A; o' m8 Q+ F
// 计算多少步之后达到最大速度的限制
// max_s_lim = speed^2 / (2*alpha*accel). g) o' [9 ~; G. m$ G+ p8 \% y I
max_s_lim = (uint32_t)(speed*speed/(A_x200*accel/10));
// 如果达到最大速度小于0.5步,我们将四舍五入为01 ]+ _6 k! {- ?6 \* |- w
// 但实际我们必须移动至少一步才能达到想要的速度( F! G- P- \# u0 Q- d' C) e
if(max_s_lim == 0){2 f( V* V6 i2 i5 V
max_s_lim = 1;
}
// n1 = (n1+n2)decel / (accel + decel)
accel_lim = (uint32_t)(step*decel/(accel+decel));- E+ }2 }" h3 C. M9 _# B
// 我们必须加速至少1步才能才能开始减速.* L+ B8 P( b( b5 G9 Z
if(accel_lim == 0){5 L6 ]! z7 H3 J; u5 Y
accel_lim = 1;
}
// 使用限制条件我们可以计算出减速阶段步数
if(accel_lim <= max_s_lim){, }! u! J+ q5 a& e/ X
srd.decel_val = accel_lim - step;1 @2 c5 ~( y$ b7 @' Z
}
else{0 ?/ c1 A% Q( H; v$ |* k/ w6 e
srd.decel_val = -(max_s_lim*accel/decel);
}
// 当只剩下一步我们必须减速
if(srd.decel_val == 0){4 M' }8 X( D+ O# g$ u: d
srd.decel_val = -1;
}% b* P! U" J! ^( A$ ~/ S
// 计算开始减速时的步数& H5 R; f; x: e4 X' } ?# S& o4 @
srd.decel_start = step + srd.decel_val;8 L0 K0 r# N2 d' D
" O$ Z' v! H$ m# a9 P: p5 T1 Z
// 如果最大速度很慢,我们就不需要进行加速运动
if(srd.step_delay <= srd.min_delay){7 J: @' G/ M& l) V9 n
srd.step_delay = srd.min_delay;
srd.run_state = RUN;4 s4 v) ]4 ?: ^# q. j5 p
}- B+ m$ v* x s( N
else{& @2 c% A# t) ] ^ Q4 @
srd.run_state = ACCEL;
} " _3 q, o% g7 u/ u1 J! b) w
// 复位加速度计数值$ R+ O! @0 n; c. |, u
srd.accel_count = 0;( ]8 z! ~! ]5 }7 M/ J* h
}
MotionStatus = 1; // 电机为运动状态
tim_count=__HAL_TIM_GET_COUNTER(&htimx_STEPMOTOR);1 C1 u* F4 M4 a2 e( |- Z9 Z& _; L
__HAL_TIM_SET_COMPARE(&htimx_STEPMOTOR,STEPMOTOR_TIM_CHANNEL_x,tim_count+srd.step_delay); // 设置定时器比较值
TIM_CCxChannelCmd(STEPMOTOR_TIMx, STEPMOTOR_TIM_CHANNEL_x, TIM_CCx_ENABLE); // 使能定时器通道
STEPMOTOR_OUTPUT_ENABLE();
}: `& F" J2 `( |$ \) [ v
1、机器人和工业驱动器
2、纺织、缝纫机. o! s& M0 y! @0 I9 _
3、包装机械
4、工厂和实验室自动化
5、高速 3D 打印机
6、液体处理! R% J2 g, d! f3 y' U2 T& j
7、医疗" A% r. C- E& Y7 k) O
8、办公自动化% `( p% }1 T+ }1 c3 R
9、有线闭路电视
10、自动取款机、现金回收# C- n4 U3 o( G
11、泵和阀门
电机驱动主芯片采用TMC2160; Q& _* o1 d6 e. A
TMC2160和5160区别:5160带UART控制及智能定位控制(如速度控制、位置控制);
TMC2160和TMC260 TMC262区别:以前版本需要通过SPI接口设置细分、电流等,新的TMC2160可通过拨码开关设置;; A% J0 P/ d: \+ U6 M) e
有条件的输入输出最好加些滤波和保护,输出加脉冲整形(比如加74HC14)- e {2 }% n! Z% k/ H
/ U4 U1 `* }, k# U) \( m2 Y
拨码开关ON:设置为高电平1,反之低电平0; Q. ?9 @7 Q) x1 i& C7 O
细分设置:CFG1、CFG0
CFG1、CFG0:
11:64细分
10:32细分! ~) d( B) v9 G: J: n
01:16细分# t; |- e" X5 p3 z
00:8细分& ^7 _1 w$ F4 _" \/ s
运行电流设置:CFG4、CFG3、CFG2& j6 a% o+ S9 _
CFG4、CFG3、CFG2:
111:IRUN=31
110:IRUN=28
101:IRUN=269 K2 R) Z6 @( [
100:IRUN=249 D: _; L Y0 t9 P& {/ J+ G
011:IRUN=22! g P3 P- W# s" ^* g' r
010:IRUN=20
001:IRUN=18
000:IRUN=16) \4 q1 |5 f: Z0 l" p3 w
Irms=Vfs/(IRUN/32)/(Rsense*1.414);
Vfs =325mV,Rsense为0.05欧时,则最大电流为4.5A左右;9 a* T3 J* D5 K. Z7 Y
CFG5:+ k' N: ~2 e+ b6 {
1:SpreadCyle模式,低速、低平稳运行模式- B g$ s0 y0 T' I# F- }
0:SpreadCyle模式,高速、高运动稳定模式+ c8 |$ @- G9 y+ v: k4 _2 ^
保持电流设置:CFG6
CFG6:# r- a' p2 |% ^- r$ X
1:保持电流=运行电流/2
0:保持电流=运行电流
DIR+STEP接口模式位置控制(无需SPI接口)+ Y- S/ Y6 J I; C" B0 L! P- X
COM端:接24V或12V或5V+ _9 n4 T' ~! T) H
DRV_ENN、STER、DIR:接集电极开路输出* }" R2 Y. h* V
为测试方便:
COM端:接3.3V+ y. C% T, e) `# L, h; w/ Z% {
DRV_ENN:使能,接PB147 B- K7 E% ?# {% F0 R
DIR:方向, PB15: }# p; {; [' p: a$ D
STEP:脉冲,PA8, W& A. `7 _) o/ t0 c' m3 i2 t5 c