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之继电器模块
微信公众号
手机版
/**
* 函数功能: 定时器中断服务函数, E; G' o6 {# r- Y0 h; Q! a8 H
* 输入参数: 无
* 返 回 值: 无
* 说 明: 实现加减速过程( k1 X% V* }2 n$ e
*/' i3 _: A* R+ @+ j9 d/ l2 h
void STEPMOTOR_TIMx_IRQHandler(void)//定时器中断处理/ R/ R& I# l# A' l5 L5 R! n3 u- c
{
__IO uint16_t tim_count=0;
// 保存新(下)一个延时周期9 q1 _8 X1 Y) g' y: J& p0 p
uint16_t new_step_delay=0;
// 加速过程中最后一次延时(脉冲周期).
__IO static uint16_t last_accel_delay=0;/ J7 b/ w! c9 T& S" h! f* ?1 Y
// 总移动步数计数器
__IO static uint32_t step_count = 0;( ?/ ^0 j* S" Z8 R; W* a
// 记录new_step_delay中的余数,提高下一步计算的精度
__IO static int32_t rest = 0;. L& d' A/ @ w# G6 z
//定时器使用翻转模式,需要进入两次中断才输出一个完整脉冲, F( U* D6 \, b$ ?; J! f* G
__IO static uint8_t i=0;, m" v; K, h3 @3 M
3 |$ B+ U& X/ t) T$ P' c$ c9 Z
if(__HAL_TIM_GET_IT_SOURCE(&htimx_STEPMOTOR, STEPMOTOR_TIM_IT_CCx) !=RESET)
{% J1 z; _/ u# {; h# y
// 清楚定时器中断
__HAL_TIM_CLEAR_IT(&htimx_STEPMOTOR, STEPMOTOR_TIM_IT_CCx);
// 设置比较值
tim_count=__HAL_TIM_GET_COUNTER(&htimx_STEPMOTOR);
__HAL_TIM_SET_COMPARE(&htimx_STEPMOTOR,STEPMOTOR_TIM_CHANNEL_x,tim_count+srd.step_delay);1 O7 }; {! Z5 |5 \
9 @5 v! _' T7 s) b: F
i++; // 定时器中断次数计数值
if(i==2) // 2次,说明已经输出一个完整脉冲
{
i=0; // 清零定时器中断次数计数值7 o4 u/ j. N# m3 V
switch(srd.run_state) // 加减速曲线阶段2 N/ Z0 j+ {; ^0 ^6 l0 m! d) O
{
case STOP:$ {, o! u4 v6 }+ A4 b
step_count = 0; // 清零步数计数器8 X! g% Q- \( M( A% r) B) c' N
rest = 0; // 清零余值
// 关闭通道
TIM_CCxChannelCmd(STEPMOTOR_TIMx, STEPMOTOR_TIM_CHANNEL_x, TIM_CCx_DISABLE);
__HAL_TIM_CLEAR_FLAG(&htimx_STEPMOTOR, STEPMOTOR_TIM_FLAG_CCx);
STEPMOTOR_OUTPUT_DISABLE();# J1 z) c% h* M- w7 h" O: J
MotionStatus = 0; // 电机为停止状态
break;
case ACCEL:
step_count++; // 步数加16 ]0 ]7 r1 A# L- v& a( v; u0 C2 f
if(srd.dir==CW)
{ ; f0 R% c' }) d* g
step_position++; // 绝对位置加1; [2 h: b6 q* S; m
}
else3 w# j% M+ [; q! [
{
step_position--; // 绝对位置减1
}0 t2 V: Y/ f2 [- ~
srd.accel_count++; // 加速计数值加1: P% A! l4 f0 O/ M
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; // 下个脉冲进入减速阶段7 ]3 k( [) _. \- t- W- o# H( g
}
else if(new_step_delay <= srd.min_delay) // 检查是否到达期望的最大速度$ j$ ]) ^- Q: R2 Q# V6 x
{
last_accel_delay = new_step_delay; // 保存加速过程中最后一次延时(脉冲周期)) N9 F( p( x& a/ W4 T+ f, r
new_step_delay = srd.min_delay; // 使用min_delay(对应最大速度speed)
rest = 0; // 清零余值
srd.run_state = RUN; // 设置为匀速运行状态5 A; ~4 T$ i5 i7 \
}0 k9 \: X7 t8 {7 G
break;
. T% @: [0 ]: N
case RUN:
step_count++; // 步数加1! s9 u' m. ^) t, R! _ ]# P8 n
if(srd.dir==CW)+ }0 T# p6 Y" p; j4 t
{
step_position++; // 绝对位置加1
}8 @' I- |+ z7 M
else
{. X7 \* i9 w% q& t! A8 E( H
step_position--; // 绝对位置减1
}( p" F" B8 x% Z5 b) |
new_step_delay = srd.min_delay; // 使用min_delay(对应最大速度speed)" @7 H3 K$ z: k. v- H
if(step_count >= srd.decel_start) // 需要开始减速$ W j' E$ V$ V7 D, c$ U r
{# f' ~$ F) ^( y# o: `
srd.accel_count = srd.decel_val; // 减速步数做为加速计数值
new_step_delay = last_accel_delay;// 加阶段最后的延时做为减速阶段的起始延时(脉冲周期)
srd.run_state = DECEL; // 状态改变为减速
}; L- g/ N! Y8 i. ?
break;" C' D" j, c/ m0 V1 _' K. @) U. ~
case DECEL:# y+ G5 P L$ D& w
step_count++; // 步数加1. o4 G% H4 i' i; g4 j4 T
if(srd.dir==CW)
{ & z5 N1 \6 a r" S: G
step_position++; // 绝对位置加1! [/ X$ k7 }& v& j# k4 a6 B C, {
}7 u' X. Q7 N& Z
else" A6 v& L: b: M- F( z
{ w* ^2 k* \; {( v$ F
step_position--; // 绝对位置减1- u/ X% F4 B9 R5 w
}$ | p' E" g' x
srd.accel_count++;3 W3 q; f7 Y, h8 O
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);// 计算余数,下次计算补上余数,减少误差3 V3 D& h; V7 @/ ]
//检查是否为最后一步. R- V9 v4 X+ N* }1 G$ D r4 w, P
if(srd.accel_count >= 0)1 |6 ~$ C) w$ m; t
{. Z6 N1 j6 E/ ?" ~: F- R$ C% p3 t3 l. }9 w
srd.run_state = STOP;8 F* ]! y/ ~- d& C
}
break;0 n/ Z/ h& T% e, H" P' @9 {
} $ \) I& |1 d8 ^+ q% l% ?
srd.step_delay = new_step_delay; // 为下个(新的)延时(脉冲周期)赋值
}
}
}- V1 h0 f( l8 B: u$ A/ u q* C
/**4 S) W3 J I! M& f& s* y
* 函数功能: 相对位置运动:运动给定的步数
* 输入参数: step:移动的步数 (正数为顺时针,负数为逆时针).
accel 加速度,实际值为accel*0.1*rad/sec^2. w* H9 n: S+ {# @% E+ f
decel 减速度,实际值为decel*0.1*rad/sec^2
speed 最大速度,实际值为speed*0.1*rad/sec
* 返 回 值: 无
* 说 明: 以给定的步数移动步进电机,先加速到最大速度,然后在合适位置开始+ s# O4 Y! _8 G. j, k0 Y
* 减速至停止,使得整个运动距离为指定的步数。如果加减速阶段很短并且6 Z- _# p) {5 f0 \6 G$ y( S: r
* 速度很慢,那还没达到最大速度就要开始减速
*/7 w7 r. e: o5 a3 a8 L" O
void STEPMOTOR_AxisMoveRel(__IO int32_t step, __IO uint32_t accel, __IO uint32_t decel, __IO uint32_t speed)0 y$ t) m4 L: t( p
{
__IO uint16_t tim_count;
// 达到最大速度时的步数0 I9 z6 Y% \0 ?$ J
__IO uint32_t max_s_lim;* t$ U, @) o! ]% O; R
// 必须要开始减速的步数(如果加速没有达到最大速度)
__IO uint32_t accel_lim;
: j Y6 u. P- u0 c
if(step < 0) // 步数为负数. |1 l C% W. d2 N" A* p" ~; v
{! u# Y8 K& |# e" P9 p) [! |
srd.dir = CCW; // 逆时针方向旋转
STEPMOTOR_DIR_REVERSAL();' w; S# }# w5 ?; ]: Q/ L8 {9 V
step =-step; // 获取步数绝对值6 Y" c$ I% D" e: L+ v y# \2 x1 W
}4 Y% F4 l; m8 s. ]; k
else) _% D @6 F. q- x- v
{* w4 o i+ X( g* q
srd.dir = CW; // 顺时针方向旋转6 Y: j) C* ~0 C2 z0 C2 L
STEPMOTOR_DIR_FORWARD();: v- I% Z8 @5 m4 |8 |
}
if(step == 1) // 步数为1
{
srd.accel_count = -1; // 只移动一步
srd.run_state = DECEL; // 减速状态.
srd.step_delay = 1000; // 短延时
}
else if(step != 0) // 如果目标运动步数不为00 p; Y* Y: S! J# g9 z3 g8 J5 y
{
// 我们的驱动器用户手册有详细的计算及推导过程
+ c, o: h* j: x3 M5 i
// 设置最大速度极限, 计算得到min_delay用于定时器的计数器的值。# S' U4 Q% k3 J3 I
// min_delay = (alpha / tt)/ w, h0 z0 P+ o C% [/ J1 s* H
srd.min_delay = (int32_t)(A_T_x10/speed);
// 通过计算第一个(c0) 的步进延时来设定加速度,其中accel单位为0.1rad/sec^27 ?( u7 o1 t& ~6 z& u: T; S
// step_delay = 1/tt * sqrt(2*alpha/accel)' r& z/ w: D. w# \. z
// step_delay = ( tfreq*0.676/10 )*10 * sqrt( (2*alpha*100000) / (accel*10) )/100" C* a" k4 _2 E% }/ ?
srd.step_delay = (int32_t)((T1_FREQ_148 * sqrt(A_SQ / accel))/10);
// 计算多少步之后达到最大速度的限制5 j; S9 N% w- @1 ^1 V) l, i
// max_s_lim = speed^2 / (2*alpha*accel): e7 F, ^7 { l4 e
max_s_lim = (uint32_t)(speed*speed/(A_x200*accel/10));
// 如果达到最大速度小于0.5步,我们将四舍五入为0& P" {5 s! Z; q! f8 k a
// 但实际我们必须移动至少一步才能达到想要的速度
if(max_s_lim == 0){% X& h; o+ O0 t, x- s4 b
max_s_lim = 1;! y3 m: p) E5 O Z3 J0 U. v
}
// n1 = (n1+n2)decel / (accel + decel)
accel_lim = (uint32_t)(step*decel/(accel+decel));7 I6 f+ ^/ T7 ]9 s+ T$ A: }
// 我们必须加速至少1步才能才能开始减速.
if(accel_lim == 0){6 g; z- u' i. g% d7 c5 Z8 l5 K
accel_lim = 1;& x2 q8 V" A* u" g! X M+ k
}
9 y$ ?' r: \# v% h; H! N* k
// 使用限制条件我们可以计算出减速阶段步数6 D* Q, q) F- z. ?
if(accel_lim <= max_s_lim){2 i, k) e5 p+ a$ }4 k2 S# M( D
srd.decel_val = accel_lim - step;
}% v8 U/ W% W6 m3 C! r
else{
srd.decel_val = -(max_s_lim*accel/decel);
}; S6 J7 }8 [) t! G1 A- p! k
// 当只剩下一步我们必须减速
if(srd.decel_val == 0){
srd.decel_val = -1;
}
// 计算开始减速时的步数3 _% O- Z$ P6 n; R
srd.decel_start = step + srd.decel_val;! y% x6 B2 h; k
// 如果最大速度很慢,我们就不需要进行加速运动
if(srd.step_delay <= srd.min_delay){
srd.step_delay = srd.min_delay;
srd.run_state = RUN;! {4 A8 \: }9 ]" n' P
}
else{( \+ U& r1 a$ m( {
srd.run_state = ACCEL;7 T3 o" Y" {0 B& g- Y7 Y1 f
}
// 复位加速度计数值
srd.accel_count = 0;2 ~5 \% l6 N+ K& H& `2 L9 h
}
MotionStatus = 1; // 电机为运动状态
tim_count=__HAL_TIM_GET_COUNTER(&htimx_STEPMOTOR);9 Y! `% ?6 M8 ]: ~
__HAL_TIM_SET_COMPARE(&htimx_STEPMOTOR,STEPMOTOR_TIM_CHANNEL_x,tim_count+srd.step_delay); // 设置定时器比较值8 G9 k# u4 r( o, g4 K
TIM_CCxChannelCmd(STEPMOTOR_TIMx, STEPMOTOR_TIM_CHANNEL_x, TIM_CCx_ENABLE); // 使能定时器通道
STEPMOTOR_OUTPUT_ENABLE();
}8 {8 E# ]5 Z8 h/ F: \+ U( T$ [
1、机器人和工业驱动器( z" Y& L$ i. D- L3 M+ W, e
2、纺织、缝纫机. e8 R. a, C+ K* _" t& e$ d
3、包装机械: u( Q, B% b/ q; N T: B2 c
4、工厂和实验室自动化
5、高速 3D 打印机
6、液体处理: s5 N6 u9 f& E1 A' }# U
7、医疗" [% ^4 @0 Z9 h+ `$ j
8、办公自动化
9、有线闭路电视& |' C W @2 r' z
10、自动取款机、现金回收/ L, M) q9 T3 }) C* `6 o8 G
11、泵和阀门
电机驱动主芯片采用TMC2160
TMC2160和5160区别:5160带UART控制及智能定位控制(如速度控制、位置控制);
TMC2160和TMC260 TMC262区别:以前版本需要通过SPI接口设置细分、电流等,新的TMC2160可通过拨码开关设置;1 j& D: C) }2 a
有条件的输入输出最好加些滤波和保护,输出加脉冲整形(比如加74HC14)+ ^3 ]: j5 a9 j0 G2 s
拨码开关ON:设置为高电平1,反之低电平0
细分设置:CFG1、CFG0! a% x5 O4 V4 v6 n
CFG1、CFG0:
11:64细分' L* n# x2 `/ [5 ^* Z
10:32细分
01:16细分: M2 u$ p, J9 ~" |
00:8细分
运行电流设置:CFG4、CFG3、CFG2
CFG4、CFG3、CFG2:' N8 ?2 c; e4 p+ p0 }+ f
111:IRUN=31
110:IRUN=282 `2 S* H- l; c! R% q
101:IRUN=268 [5 g7 g6 r0 T; G
100:IRUN=24
011:IRUN=22" l# p) I/ t( C% i
010:IRUN=20
001:IRUN=18/ I; k$ y3 Z: n9 j7 M) D8 B. r
000:IRUN=16: y; i0 H1 l9 ?! v
Irms=Vfs/(IRUN/32)/(Rsense*1.414);
Vfs =325mV,Rsense为0.05欧时,则最大电流为4.5A左右;
CFG5:8 A3 H7 w9 s: c& u4 v' \& x# m
1:SpreadCyle模式,低速、低平稳运行模式
0:SpreadCyle模式,高速、高运动稳定模式6 H5 l0 R2 r& U6 B9 N# d' i" @8 v
保持电流设置:CFG6
CFG6:
1:保持电流=运行电流/2
0:保持电流=运行电流* L% C3 Z$ x: Z8 Y- h& a3 s" X
DIR+STEP接口模式位置控制(无需SPI接口)2 u# o/ S( n% F; L" r, u3 K7 A) X0 X
COM端:接24V或12V或5V# A7 f3 f4 j% p# u# R
DRV_ENN、STER、DIR:接集电极开路输出
为测试方便:1 E+ |/ `' s0 X$ U
COM端:接3.3V
DRV_ENN:使能,接PB14
DIR:方向, PB15
STEP:脉冲,PA8