步进电机梯形加减速开环速度模式的实现
背景介绍
很多介绍步进电机梯形加减速的都源于参考书目《AVR446_Linear speed control of stepper motor.pdf》,正点原子,野火,硬石等都有相应的教程,都讲了固定脉冲数时的梯形加减速原理及对应程序。
在实际项目中,步进除了工作于精确的位置模式,有时还需要步进电机工作于速度模式,并可以方便的更改其旋转速度。本文主要解决步进工作在速度模式下如何通过梯形加减速来实现变速运行。
梯形加减速算法原理详解
梯形加减速详细的算法原理有很多资料参考,不再本文范围内,后续主要介绍梯形加减速如何工作在速度模式下。基本原理介绍可以参考此处步进电机梯形加减速实现
如何工作于速度模式下
只需要将工作模式设置于速度模式,加速完毕后,一直处于RUN即可
步进处于运行时,如何更改速度及加速度
根据新速度,及步进当前速度,判断步进应加速,减速或运行。
当处于RUN时,当前速度就是之前的设定速度。
当处于加减速时 当前速度可以由 下面公式获得。
cur_speed = (int32_t)(A_T_x10 /step_delay);
根据新旧加减速度,判断是否更新accel_count
通过公式8我们可以计算出新的加速时,对应的C0值,注意此处无需乘以0.676这个修正系数。
通过公式9,我们可以反算出脉冲周期数n=(Cn/C0-C0/Cn)^2/4
Cn是我们当前的定时器周期计数值,C0由新的加速度计算获得。我们就可以得到当前速度对应于新的加速度时所处的脉冲周期数n。用n更新accel_count即可改变加速度大小。
注意此方法忽略了rest的影响。
具体实现代码
#define T1_FREQ_148_NO_676 ((float)((T1_FREQ ) / 10))
/// @brief 计算当前速度在新的加速度下的位于第n次脉冲
/// @param cur_delay 当前速度对应的定时器计数值
/// @param accel 新的加速度
/// @return 返回当前速度在新的加速度下的脉冲数
uint32_t get_Accel_Count(uint32_t cur_delay, uint32_t accel)
{
int32_t C0_delay;
float ftemp;
// 计算C0
C0_delay = (int32_t)((T1_FREQ_148_NO_676 * sqrt(A_SQ / accel)) / 10);
// 公式Cn/C0=(sqrt(n+1)-sqrt(n)) 逆运算 n=(Cn/C0-C0/Cn)^2/4
// 计算出当前速度(定时器计数值cur_delay)对应的n
ftemp = (float)cur_delay / C0_delay - (float)C0_delay / cur_delay;
// 由新的accel更新accel_count
return (ftemp * ftemp) / 4;
}
/// @brief 电机旋转函数
/// @param speed 速度 speed更改大于SPEED_THRESHOLD才有效
/// @param accel 加减速 accel更改大于ACCEL_THRESHOLD才有效 speed大于当前速度 则表示加速度 speed小于当前速度 则表示减速度
/// @param dir 方向 仅启动时有效,在更改速度时,无效。如需更改方向,需要先停止再换向。
/// @param channel 通道号
void STEPMOTOR_Rotate(uint32_t speed, uint32_t accel, uint8_t dir, uint8_t channel)
{
stcPluseChannelState *pPlusChState;
pPlusChState = &pPluseChannel[channel].State;
// 达到最大速度时的步数
// uint32_t max_s_lim;
uint32_t cur_speed;
uint32_t old_speed;
uint32_t old_accel;
if (pPluseChannel[channel].Para.movemode == FIXEDLENGTH_MODE && pPlusChState->run_state != STOP)
return;
if (pPlusChState->run_state == STOP)
{
if (speed == 0 || accel == 0)
return;
// 记录参数
pPluseChannel[channel].Para.dir = dir;
pPluseChannel[channel].Para.movemode = SPEED_MODE;
pPluseChannel[channel].Para.set_speed = speed;
pPluseChannel[channel].Para.step_accel = accel;
pPluseChannel[channel].Para.step_decel = accel;
pPlusChState->dir = dir;
pPlusChState->steps = 0;
pPlusChState->step_count = 0;
// pPlusChState->step_position = 0;
pPlusChState->min_delay = (int32_t)(A_T_x10 / speed);
pPlusChState->step_delay = (int32_t)((T1_FREQ_148 * sqrt(A_SQ / accel)) / 10);
pPlusChState->run_state