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之继电器模块
微信公众号
手机版
/*** l8 T3 y6 ~3 [8 u
* 函数功能: 定时器中断服务函数& k. {9 S0 w/ G: o1 G- c5 }
* 输入参数: 无
* 返 回 值: 无
* 说 明: 实现加减速过程
*/
void STEPMOTOR_TIMx_IRQHandler(void)//定时器中断处理& I: G g# E4 V a' ~! X' f1 @; ^
{ # Z9 a# r( y3 d- m0 B. P) S
__IO uint16_t tim_count=0;% `: Z& v. |9 Q4 Z
// 保存新(下)一个延时周期0 e$ Q7 h' ` {/ J0 U+ f( }
uint16_t new_step_delay=0;
// 加速过程中最后一次延时(脉冲周期).
__IO static uint16_t last_accel_delay=0;% c1 l4 G. H4 h1 B$ l( ^- W" h
// 总移动步数计数器
__IO static uint32_t step_count = 0;
// 记录new_step_delay中的余数,提高下一步计算的精度( t$ O( u$ u6 _
__IO static int32_t rest = 0;
//定时器使用翻转模式,需要进入两次中断才输出一个完整脉冲
__IO static uint8_t i=0;
5 @7 [9 l! ^7 J
if(__HAL_TIM_GET_IT_SOURCE(&htimx_STEPMOTOR, STEPMOTOR_TIM_IT_CCx) !=RESET)
{. A8 a3 h' l9 J& z' m4 e8 X
// 清楚定时器中断
__HAL_TIM_CLEAR_IT(&htimx_STEPMOTOR, STEPMOTOR_TIM_IT_CCx);, p8 x0 g& o; g K3 e
: z$ }; F, T8 N4 I1 d f i, u9 G
// 设置比较值- ?! M9 [: k b$ G G* A: t3 H
tim_count=__HAL_TIM_GET_COUNTER(&htimx_STEPMOTOR);4 i; x* x: ?: V1 l% Y! B% a
__HAL_TIM_SET_COMPARE(&htimx_STEPMOTOR,STEPMOTOR_TIM_CHANNEL_x,tim_count+srd.step_delay);
i++; // 定时器中断次数计数值
if(i==2) // 2次,说明已经输出一个完整脉冲
{
i=0; // 清零定时器中断次数计数值& v- m9 E( q b$ R4 ^8 D2 R/ B0 l$ r9 ?5 x
switch(srd.run_state) // 加减速曲线阶段
{
case STOP:
step_count = 0; // 清零步数计数器# K0 Y) X9 M* N0 {
rest = 0; // 清零余值
// 关闭通道4 A" X" {0 @6 s/ ?: \3 ^. \
TIM_CCxChannelCmd(STEPMOTOR_TIMx, STEPMOTOR_TIM_CHANNEL_x, TIM_CCx_DISABLE); 3 r) k2 z3 b6 q9 d( [. a$ s
__HAL_TIM_CLEAR_FLAG(&htimx_STEPMOTOR, STEPMOTOR_TIM_FLAG_CCx);
STEPMOTOR_OUTPUT_DISABLE();+ a. P# U t. r2 _9 B E# S
MotionStatus = 0; // 电机为停止状态
break;) C' Q1 u/ F2 ~ v+ C
case ACCEL:
step_count++; // 步数加1/ i4 ^' `! f8 z/ B4 x7 P
if(srd.dir==CW)
{ 3 D4 ]) R) x( I0 J9 U6 E9 q) p1 M
step_position++; // 绝对位置加1. f( f* e3 f1 X# L0 g! ^6 A5 u5 o3 N
}: t( |( P9 j# q7 z0 ~& f( S, _
else
{4 |/ u* v6 `# A6 W1 O+ M/ n5 |
step_position--; // 绝对位置减1
}
srd.accel_count++; // 加速计数值加1/ b' M3 v$ T+ D) J' }
new_step_delay = srd.step_delay - (((2 *srd.step_delay) + rest)/(4 * srd.accel_count + 1));//计算新(下)一步脉冲周期(时间间隔)
rest = ((2 * srd.step_delay)+rest)%(4 * srd.accel_count + 1);// 计算余数,下次计算补上余数,减少误差
if(step_count >= srd.decel_start)// 检查是够应该开始减速
{
srd.accel_count = srd.decel_val; // 加速计数值为减速阶段计数值的初始值
srd.run_state = DECEL; // 下个脉冲进入减速阶段
}
else if(new_step_delay <= srd.min_delay) // 检查是否到达期望的最大速度( G9 }% c0 K1 e: {1 p/ d! J
{
last_accel_delay = new_step_delay; // 保存加速过程中最后一次延时(脉冲周期)! V0 |8 Q2 C- \- h# V G0 p) q
new_step_delay = srd.min_delay; // 使用min_delay(对应最大速度speed)4 [2 ]1 ?; d; k& \) P
rest = 0; // 清零余值 S1 ~2 C: J# g2 M" i( ~9 l
srd.run_state = RUN; // 设置为匀速运行状态
}+ T9 v+ V, p) q
break;* T" s' X5 Q; \, Y
case RUN:
step_count++; // 步数加1; y1 |8 q: a$ }7 F3 _7 Y8 {3 R
if(srd.dir==CW)2 B& }6 n( T( h. F
{ : O( Y" Z/ ]6 K2 g; t' n0 a& l; b
step_position++; // 绝对位置加1( s+ d* h& G, d) V7 @( }% q; F
}
else
{& E' V- K" Q) k) _0 k$ h2 \+ q
step_position--; // 绝对位置减1
}
new_step_delay = srd.min_delay; // 使用min_delay(对应最大速度speed)% w+ ]5 o( Y5 j0 m
if(step_count >= srd.decel_start) // 需要开始减速& u+ Z8 \# Y9 V4 z: z- W6 H
{
srd.accel_count = srd.decel_val; // 减速步数做为加速计数值' V1 e( k; t/ h S6 [8 U
new_step_delay = last_accel_delay;// 加阶段最后的延时做为减速阶段的起始延时(脉冲周期)% G7 j' G; ~9 D& s. q; r! L
srd.run_state = DECEL; // 状态改变为减速
}
break;; W" F+ `- g$ ?8 q& u
' P, S+ S j0 Q4 P$ x& h7 o& f
case DECEL:9 @3 l. J: l& p
step_count++; // 步数加1' U& s( v* |% l- x9 y/ K
if(srd.dir==CW)
{ & G0 D3 c6 p! |9 s# D* f5 @
step_position++; // 绝对位置加1# `7 o8 e% }0 n
}
else4 R% n: a) }: F1 D* Q+ h9 Y
{
step_position--; // 绝对位置减17 Z5 h, P g8 a
}
srd.accel_count++;% G/ R) o Q/ _* Q7 V
new_step_delay = srd.step_delay - (((2 * srd.step_delay) + rest)/(4 * srd.accel_count + 1)); //计算新(下)一步脉冲周期(时间间隔)
rest = ((2 * srd.step_delay)+rest)%(4 * srd.accel_count + 1);// 计算余数,下次计算补上余数,减少误差- e0 A1 s4 Z7 R2 l2 [4 i+ e
//检查是否为最后一步# F% Q3 s9 A7 i+ V( `) P1 I
if(srd.accel_count >= 0)$ d. C& }( |7 e! M) o% e" B
{0 O2 t: r( T" [, |* L! V
srd.run_state = STOP;6 l- A( f! @5 j& u/ d, @1 P
}+ ^7 p* b5 c$ O) \: `3 x
break;
}
srd.step_delay = new_step_delay; // 为下个(新的)延时(脉冲周期)赋值. E% `. Y& @; w6 A# T9 B
}
}' L- z% c% h. [; Z) w0 z4 f, x
}- g9 K g% J: V* ?9 x
/**$ l4 b3 W) w* z4 ~) `4 w8 E
* 函数功能: 相对位置运动:运动给定的步数0 X5 C$ J* q" H9 g2 `0 d" r
* 输入参数: step:移动的步数 (正数为顺时针,负数为逆时针).
accel 加速度,实际值为accel*0.1*rad/sec^2
decel 减速度,实际值为decel*0.1*rad/sec^2
speed 最大速度,实际值为speed*0.1*rad/sec6 a: s) d+ p& N: e, j
* 返 回 值: 无# n. Q) D. H" l" E" ^' x& k$ k
* 说 明: 以给定的步数移动步进电机,先加速到最大速度,然后在合适位置开始6 M+ c4 q) w# G: U0 R
* 减速至停止,使得整个运动距离为指定的步数。如果加减速阶段很短并且
* 速度很慢,那还没达到最大速度就要开始减速$ v; u7 A! ^+ z+ S4 s( Z
*/
void STEPMOTOR_AxisMoveRel(__IO int32_t step, __IO uint32_t accel, __IO uint32_t decel, __IO uint32_t speed)
{
__IO uint16_t tim_count;
// 达到最大速度时的步数8 C Y% s5 h6 ^5 a$ y
__IO uint32_t max_s_lim;. f# Z$ l6 o. B- t6 G% U0 w
// 必须要开始减速的步数(如果加速没有达到最大速度) _6 E0 {; F$ k# ~$ ^: w8 t
__IO uint32_t accel_lim;
if(step < 0) // 步数为负数
{
srd.dir = CCW; // 逆时针方向旋转
STEPMOTOR_DIR_REVERSAL();
step =-step; // 获取步数绝对值
}. l* j& _! f' x
else: }7 e3 t" X/ P1 E
{% W0 O% j1 p# B: [; G# }& c+ T
srd.dir = CW; // 顺时针方向旋转
STEPMOTOR_DIR_FORWARD();) ~+ R( G B1 Q: _* q6 A
}. G A9 [) U1 n& O- D
7 M" C: u% j9 Z' k" B# r" I y
if(step == 1) // 步数为1
{
srd.accel_count = -1; // 只移动一步/ V+ V# C: [% M2 ^0 N( U
srd.run_state = DECEL; // 减速状态.1 h; O# B; c8 f) S
srd.step_delay = 1000; // 短延时 6 V( W* U6 I: f% m N1 L% R8 z
}
else if(step != 0) // 如果目标运动步数不为09 R3 m* j$ O" i4 Q5 n/ \) y# g
{7 o; y( [8 m5 V1 E8 z7 t
// 我们的驱动器用户手册有详细的计算及推导过程" [5 e4 g7 x2 v0 E$ t
// 设置最大速度极限, 计算得到min_delay用于定时器的计数器的值。: [- \; ]( Q8 R+ U1 W U+ v9 Q
// min_delay = (alpha / tt)/ w
srd.min_delay = (int32_t)(A_T_x10/speed);
. L F3 Q" g' B% `# j& b
// 通过计算第一个(c0) 的步进延时来设定加速度,其中accel单位为0.1rad/sec^2% P# d$ Z, z: d2 a$ U# f- b3 y, F
// step_delay = 1/tt * sqrt(2*alpha/accel)
// step_delay = ( tfreq*0.676/10 )*10 * sqrt( (2*alpha*100000) / (accel*10) )/100/ \8 ^6 t2 x& `: v2 P
srd.step_delay = (int32_t)((T1_FREQ_148 * sqrt(A_SQ / accel))/10);
// 计算多少步之后达到最大速度的限制
// max_s_lim = speed^2 / (2*alpha*accel)
max_s_lim = (uint32_t)(speed*speed/(A_x200*accel/10));
// 如果达到最大速度小于0.5步,我们将四舍五入为0
// 但实际我们必须移动至少一步才能达到想要的速度. W) \9 c. V0 k" y
if(max_s_lim == 0){
max_s_lim = 1;, A) n# D2 o& H; R/ A
}
// n1 = (n1+n2)decel / (accel + decel)' O: B% Z" B, A$ ^
accel_lim = (uint32_t)(step*decel/(accel+decel));
// 我们必须加速至少1步才能才能开始减速.
if(accel_lim == 0){
accel_lim = 1;
}9 t$ i' z: ~# Y. M8 d: @
% l# a) M4 i3 \7 g
// 使用限制条件我们可以计算出减速阶段步数
if(accel_lim <= max_s_lim){
srd.decel_val = accel_lim - step;4 \/ ]/ X1 |0 H' z
}! L1 ^9 d4 ?* a5 x
else{
srd.decel_val = -(max_s_lim*accel/decel);' e$ F1 s3 O# W/ G2 Z$ w5 j# o
}3 r* l1 a/ o0 r% [
// 当只剩下一步我们必须减速' A+ J2 V9 Y9 N5 K
if(srd.decel_val == 0){+ ^, ? T$ [5 ^4 ]1 t7 P
srd.decel_val = -1;
}: | R% F6 Z& K% U. p
// 计算开始减速时的步数8 X- T# \2 d. j" ^
srd.decel_start = step + srd.decel_val;
// 如果最大速度很慢,我们就不需要进行加速运动0 O# C2 f0 N6 _2 i4 ~7 z
if(srd.step_delay <= srd.min_delay){
srd.step_delay = srd.min_delay;6 J2 Y6 `) ?# s: j+ w3 n" L
srd.run_state = RUN;
}
else{3 f9 m1 D! G' n1 h h; ?5 q
srd.run_state = ACCEL;3 i+ e0 f# @: x! @, c o- c' V' a
}
// 复位加速度计数值
srd.accel_count = 0;, x- H( S* f& L3 h }' R! ?
} x& [% Q' B' V" P! {5 J1 J' ^
MotionStatus = 1; // 电机为运动状态
tim_count=__HAL_TIM_GET_COUNTER(&htimx_STEPMOTOR);- t" z' n5 a- \7 i
__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();
}
1、机器人和工业驱动器% k! t/ w3 H7 c& D: l& D( b$ _
2、纺织、缝纫机2 C* L, o; S0 P& S
3、包装机械: N2 s3 F( s8 H7 @6 m
4、工厂和实验室自动化1 l, H& a1 \" g2 \
5、高速 3D 打印机
6、液体处理6 K6 w9 a6 p a/ T
7、医疗 U4 b! T( j( q0 u' M" A" ?* W* o
8、办公自动化
9、有线闭路电视
10、自动取款机、现金回收6 f: R# m* R# q, M
11、泵和阀门
电机驱动主芯片采用TMC2160* [, W& ]+ p+ n& A. U
TMC2160和5160区别:5160带UART控制及智能定位控制(如速度控制、位置控制);
TMC2160和TMC260 TMC262区别:以前版本需要通过SPI接口设置细分、电流等,新的TMC2160可通过拨码开关设置;) M9 ^& l; _9 E
有条件的输入输出最好加些滤波和保护,输出加脉冲整形(比如加74HC14)
' f2 @- J7 f3 @+ X6 \8 @3 T
拨码开关ON:设置为高电平1,反之低电平0
细分设置:CFG1、CFG0
CFG1、CFG0:
11:64细分
10:32细分 S9 M/ A4 g* f& J/ ^% \/ l% }1 k
01:16细分 M8 Z: d2 W+ }& X9 {2 ]2 i1 l
00:8细分# @# R! M+ ]( a3 A$ \
运行电流设置:CFG4、CFG3、CFG2
CFG4、CFG3、CFG2:! p4 u2 D) a' } g4 Y8 }
111:IRUN=31% |- _; J( K# k
110:IRUN=28% H4 Y9 z. a; b1 _+ U1 z
101:IRUN=26
100:IRUN=24
011:IRUN=22
010:IRUN=20
001:IRUN=18
000:IRUN=16
Irms=Vfs/(IRUN/32)/(Rsense*1.414);
Vfs =325mV,Rsense为0.05欧时,则最大电流为4.5A左右;
CFG5:
1:SpreadCyle模式,低速、低平稳运行模式0 U9 @% q8 {2 i! A) c, z
0:SpreadCyle模式,高速、高运动稳定模式5 n1 k2 I9 w! ~8 p+ s
保持电流设置:CFG6
CFG6:7 H0 [5 C! X, ]2 v$ g
1:保持电流=运行电流/2! ^% n! `7 q# l& P: W" j. m
0:保持电流=运行电流% P5 [$ i& {1 I" \: s6 W R
DIR+STEP接口模式位置控制(无需SPI接口)
COM端:接24V或12V或5V
DRV_ENN、STER、DIR:接集电极开路输出
为测试方便:
COM端:接3.3V8 t3 P# L! t; A- A
DRV_ENN:使能,接PB14
DIR:方向, PB15! B. \) R" E. v; @; a) |( k& M& l3 k
STEP:脉冲,PA87 B! p% I8 \+ L+ x1 W& }$ Y% X% R