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之继电器模块
微信公众号
手机版
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART2_UART_Init();
MX_TIM1_Init();
STEPMOTOR_TIMx_Init(); /定时器输出初始化
/* USER CODE BEGIN 2 */; f4 M9 m- H2 {2 j0 V) J
STEPMOTOR_OUTPUT_DISABLE(); //TMC2160禁止使能; `0 d5 l8 F7 k( a0 D& F$ y. t
/* USER CODE END 2 */* w- q4 v& u# x! s4 C, C/ V
/* Infinite loop */ Z- S$ U- c3 s. f+ f) H3 v
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */# m n2 W/ k5 R1 J1 a. M
STEPMOTOR_AxisMoveRel(6400*-2, 5000 , 5000 , 1200); //控制TMC2160驱动步进电机反转2圈(32细分,1.8度则一圈需要6400个脉冲)4 i& j0 P) ~8 U
LedOnOff(); //LED闪烁处理6 A, N. E; j m M$ N8 T# i
STEPMOTOR_AxisMoveRel(6400*2, 5000 , 5000 , 1200); //控制TMC2160驱动步进电机反转2圈(32细分,1.8度则一圈需要6400个脉冲)
LedOnOff(); //LED闪烁处理+ _/ }* O, k& l4 E/ g. { a
/* USER CODE BEGIN 3 */
}
/**
* 函数功能: 相对位置运动:运动给定的步数
* 输入参数: step:移动的步数 (正数为顺时针,负数为逆时针).
accel 加速度,实际值为accel*0.1*rad/sec^2* t4 _4 J* t. Z- ?. h, |- b. g1 ^
decel 减速度,实际值为decel*0.1*rad/sec^29 U& T( T3 n; z ]% s. q8 }
speed 最大速度,实际值为speed*0.1*rad/sec
* 返 回 值: 无/ @. }6 o) I# c; E; r
* 说 明: 以给定的步数移动步进电机,先加速到最大速度,然后在合适位置开始
* 减速至停止,使得整个运动距离为指定的步数。如果加减速阶段很短并且
* 速度很慢,那还没达到最大速度就要开始减速
*/
void STEPMOTOR_AxisMoveRel(__IO int32_t step, __IO uint32_t accel, __IO uint32_t decel, __IO uint32_t speed)* T/ x3 C( ~! E& a$ e4 w3 r8 h3 X. }
{
__IO uint16_t tim_count;
// 达到最大速度时的步数$ x7 R/ X" F8 L3 H! B7 `5 I
__IO uint32_t max_s_lim;9 ?; t7 P3 B p0 M
// 必须要开始减速的步数(如果加速没有达到最大速度)7 L7 B5 _$ ]4 z" R" Y! b
__IO uint32_t accel_lim;
if(step < 0) // 步数为负数% G/ p, n, ?& ]8 P' s0 f3 O
{
srd.dir = CCW; // 逆时针方向旋转
STEPMOTOR_DIR_REVERSAL();
step =-step; // 获取步数绝对值
}
else
{+ e" ]" @4 x4 G3 z
srd.dir = CW; // 顺时针方向旋转
STEPMOTOR_DIR_FORWARD();/ S n7 B" ]( f6 n
}
0 Q n6 q. }+ I; E
if(step == 1) // 步数为19 l2 Z9 _- F8 \5 }
{: d& y5 Q9 t- A& @9 u6 o
srd.accel_count = -1; // 只移动一步
srd.run_state = DECEL; // 减速状态.( I6 d% Y, L" t% y3 Q
srd.step_delay = 1000; // 短延时
}8 z* Z, X+ d% D3 x
else if(step != 0) // 如果目标运动步数不为0
{5 M8 A0 ]/ H( O7 @0 X
// 我们的驱动器用户手册有详细的计算及推导过程
// 设置最大速度极限, 计算得到min_delay用于定时器的计数器的值。7 T& g# v8 ?/ V, i6 I
// min_delay = (alpha / tt)/ w
srd.min_delay = (int32_t)(A_T_x10/speed);) p+ `% }: |- r; f& e
8 m+ d& v. e1 f5 `8 R; P" ~
// 通过计算第一个(c0) 的步进延时来设定加速度,其中accel单位为0.1rad/sec^2
// step_delay = 1/tt * sqrt(2*alpha/accel)
// step_delay = ( tfreq*0.676/10 )*10 * sqrt( (2*alpha*100000) / (accel*10) )/100
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));: m+ \- a: q! z& O
// 如果达到最大速度小于0.5步,我们将四舍五入为06 h# K; Z) c k$ y4 v
// 但实际我们必须移动至少一步才能达到想要的速度 Y; y, W* ^' Z! R) m
if(max_s_lim == 0){
max_s_lim = 1;8 }$ ]* d. F O0 q1 U8 X$ G
}) [2 M/ |) A" a( H7 ~2 G
// n1 = (n1+n2)decel / (accel + decel)
accel_lim = (uint32_t)(step*decel/(accel+decel));( ] S$ {% F! M& P8 v
// 我们必须加速至少1步才能才能开始减速.2 M3 t' E$ c( o4 P
if(accel_lim == 0){1 Y, P0 U% P% o$ }1 r
accel_lim = 1;9 v, z5 h3 I3 _- R2 s
} _# n1 H. l7 k! `! U" E& d" o
7 F" o) s% ~' Z$ U
// 使用限制条件我们可以计算出减速阶段步数, b) v# d* J" D0 Y4 v3 x
if(accel_lim <= max_s_lim){
srd.decel_val = accel_lim - step;/ M( e2 `$ F& [+ m
}* |7 `( ], v6 k% v- O$ y% P
else{
srd.decel_val = -(max_s_lim*accel/decel);) R' K4 i1 N! {8 D2 M
}1 [$ b, M6 @2 t' F# k/ J
// 当只剩下一步我们必须减速2 C( D& X: E& w5 E
if(srd.decel_val == 0){8 x% q D L/ u
srd.decel_val = -1; k* X+ u# S8 I1 f$ f2 K& T
}+ F4 p b7 }+ u4 c& y+ H7 [2 f1 A8 r
// 计算开始减速时的步数
srd.decel_start = step + srd.decel_val;
// 如果最大速度很慢,我们就不需要进行加速运动7 e# d1 d! k' F$ E0 r6 F
if(srd.step_delay <= srd.min_delay){, N3 o* [- D5 S+ T% |& n
srd.step_delay = srd.min_delay;
srd.run_state = RUN;
}
else{3 X! h& V: d& n1 U- k
srd.run_state = ACCEL;, T% R$ `+ B8 `: w
} ) [! _( m7 S, f- _5 r2 K
// 复位加速度计数值
srd.accel_count = 0;
}
MotionStatus = 1; // 电机为运动状态. N$ G, o2 D, d& [2 e
tim_count=__HAL_TIM_GET_COUNTER(&htimx_STEPMOTOR);
__HAL_TIM_SET_COMPARE(&htimx_STEPMOTOR,STEPMOTOR_TIM_CHANNEL_x,tim_count+srd.step_delay); // 设置定时器比较值 r( H5 r6 }2 G& w4 y! Y# I
TIM_CCxChannelCmd(STEPMOTOR_TIMx, STEPMOTOR_TIM_CHANNEL_x, TIM_CCx_ENABLE); // 使能定时器通道 $ |0 @0 k% S1 c
STEPMOTOR_OUTPUT_ENABLE();
}
/**
* 函数功能: 定时器中断服务函数
* 输入参数: 无
* 返 回 值: 无, |9 i3 S7 h( d+ v u R
* 说 明: 实现加减速过程
*/8 a' p4 K h9 d w6 F' A/ l! f& H% {
void STEPMOTOR_TIMx_IRQHandler(void)//定时器中断处理
{ * O. s# k; Q, m- c4 c/ k8 ~
__IO uint16_t tim_count=0;
// 保存新(下)一个延时周期, w2 u k% r. w; r
uint16_t new_step_delay=0;
// 加速过程中最后一次延时(脉冲周期).9 [1 j1 Q* Y% y( w l
__IO static uint16_t last_accel_delay=0;$ y, }$ B3 r7 O, |/ m
// 总移动步数计数器
__IO static uint32_t step_count = 0;! Z: q9 R6 P- r' x- p" z
// 记录new_step_delay中的余数,提高下一步计算的精度
__IO static int32_t rest = 0;3 ~- K; A! _8 x) R
//定时器使用翻转模式,需要进入两次中断才输出一个完整脉冲! k" [# C9 z8 ^5 I9 `
__IO static uint8_t i=0;
* s( D3 { _( c/ Z7 b
if(__HAL_TIM_GET_IT_SOURCE(&htimx_STEPMOTOR, STEPMOTOR_TIM_IT_CCx) !=RESET)
{
// 清楚定时器中断) Y! q- d) Y& x; S( d
__HAL_TIM_CLEAR_IT(&htimx_STEPMOTOR, STEPMOTOR_TIM_IT_CCx);4 x" O) q! G& ~$ h
7 c$ T% k4 B0 K* _' ~
// 设置比较值4 q. |* x# F2 E& v' g' ^: S
tim_count=__HAL_TIM_GET_COUNTER(&htimx_STEPMOTOR);- V/ f; Y4 z+ n9 g
__HAL_TIM_SET_COMPARE(&htimx_STEPMOTOR,STEPMOTOR_TIM_CHANNEL_x,tim_count+srd.step_delay);6 o) X @/ ~3 J" D
i++; // 定时器中断次数计数值6 n; D2 s+ G# C! f
if(i==2) // 2次,说明已经输出一个完整脉冲
{
i=0; // 清零定时器中断次数计数值
switch(srd.run_state) // 加减速曲线阶段: h$ N, t. I" }
{
case STOP:1 \0 Q& [7 {5 ]# u$ j6 [5 @
step_count = 0; // 清零步数计数器
rest = 0; // 清零余值* x2 \" ?6 v d* e. G9 r( q
// 关闭通道
TIM_CCxChannelCmd(STEPMOTOR_TIMx, STEPMOTOR_TIM_CHANNEL_x, TIM_CCx_DISABLE);
__HAL_TIM_CLEAR_FLAG(&htimx_STEPMOTOR, STEPMOTOR_TIM_FLAG_CCx);& q. d- |6 G! L) R& \4 }3 X- d
STEPMOTOR_OUTPUT_DISABLE();
MotionStatus = 0; // 电机为停止状态 7 i0 K5 P' `9 P
break;, P4 y) c" Q- z4 Z) B' r5 }; }2 D' e: J
1 R& U _1 ]! b: ]5 P
case ACCEL:
step_count++; // 步数加1' D( ^! P U" h+ v2 T
if(srd.dir==CW); }' }2 c" m& W! V/ ]1 a1 H* N: d
{
step_position++; // 绝对位置加1
}
else7 Q5 o6 H" `3 f8 R% g
{" k. v6 q, M4 m$ r0 S
step_position--; // 绝对位置减1
}
srd.accel_count++; // 加速计数值加17 ^8 N4 X4 l+ C$ c) a% P) Q$ @+ C/ o0 {
new_step_delay = srd.step_delay - (((2 *srd.step_delay) + rest)/(4 * srd.accel_count + 1));//计算新(下)一步脉冲周期(时间间隔)5 Y9 V: e7 _0 x7 i, U1 K* J
rest = ((2 * srd.step_delay)+rest)%(4 * srd.accel_count + 1);// 计算余数,下次计算补上余数,减少误差
if(step_count >= srd.decel_start)// 检查是够应该开始减速
{, i8 x( \1 \. R; O# j _ [( A% Y4 _
srd.accel_count = srd.decel_val; // 加速计数值为减速阶段计数值的初始值 G8 S8 h5 F* H' }9 `5 u' o
srd.run_state = DECEL; // 下个脉冲进入减速阶段8 u; M8 B- R$ n' F4 N% f5 O
}
else if(new_step_delay <= srd.min_delay) // 检查是否到达期望的最大速度
{
last_accel_delay = new_step_delay; // 保存加速过程中最后一次延时(脉冲周期)
new_step_delay = srd.min_delay; // 使用min_delay(对应最大速度speed)
rest = 0; // 清零余值# t/ q. D8 i1 h% T7 e. R6 `
srd.run_state = RUN; // 设置为匀速运行状态
}
break; r7 y' k4 `+ s' S
case RUN:) r' w) T$ y6 ~4 B$ U
step_count++; // 步数加1
if(srd.dir==CW)
{ & c5 z. n- E+ z; \( D2 ~1 z1 {
step_position++; // 绝对位置加1
}
else
{6 E5 g8 b3 E: Y; I8 s/ X8 Q* k* M% ^- n
step_position--; // 绝对位置减1
}5 [) ]* G8 L& d! w- J5 [6 y, Z
new_step_delay = srd.min_delay; // 使用min_delay(对应最大速度speed)3 {% s& ]* O* o' r R; h/ E9 ~
if(step_count >= srd.decel_start) // 需要开始减速& r# A: z( W0 }# O! l3 m
{
srd.accel_count = srd.decel_val; // 减速步数做为加速计数值
new_step_delay = last_accel_delay;// 加阶段最后的延时做为减速阶段的起始延时(脉冲周期)
srd.run_state = DECEL; // 状态改变为减速
}
break;
. N# }5 c* Z/ a* b- Q+ [
case DECEL:
step_count++; // 步数加1' n6 V8 R$ j! U/ m7 X( s
if(srd.dir==CW)
{
step_position++; // 绝对位置加1! u4 c: p4 f$ q& F" T: J7 W
}
else
{
step_position--; // 绝对位置减1
}* X2 E9 O* j; \% S
srd.accel_count++;
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);// 计算余数,下次计算补上余数,减少误差
//检查是否为最后一步9 Q( E. H5 L, q0 y& f$ r
if(srd.accel_count >= 0)
{# Z- v: S7 n9 K/ X
srd.run_state = STOP;
}$ _) [; \1 r8 ]- v6 \$ e$ ~
break;, m6 ~7 |2 F- d6 @5 x2 x
}
srd.step_delay = new_step_delay; // 为下个(新的)延时(脉冲周期)赋值
}
}8 P5 L* X) w0 B$ g
}6 T& F0 n3 I2 c- \/ F9 g
如果确实需要原理图和PCB的用户,请联系我们的客服或管理员私下索取谢谢!( ]4 c2 e8 g& O. H
技术群:1718975842 _/ h* |, M% w/ V1 @* R
公众号:游名开源
是的,参考他们的,有说明:写着参考硬石的