本文主要实现S形加减速算法。

更新(22-06-18)

正点原子官6月17日新上线了《电机控制专题例程》,可以免费下载和观看,正点原子STM32点击应用控制系列教程。
我已经详细地看了一遍正点原子官方有关梯形加减速、S形加减速的例程,他们写的更加有逻辑性,我会按照他们例程对我的程序进行修改。

问题补充(22-06-09)

感谢csdn105GUODAO的提问,有关于输出比较模式下寄存器ARR的值的作用,以及为什么ARR要设置为0xFFFF?

下面是我找到的原因,供大家参考交流,如有错误欢迎指出。

  • 首先应用的是定时器的输出比较模式,例程使用的是TIM3的通道1,针对TIM4_CCMR1寄存器位[6:4] OC1M = 011,即翻转模式,当TIMx_CNT = TIMx_CCR1时,OC1REF发生翻转;
  • 其次,在定时器中断程序中,查询的标志位宏定义 TIM_IT_CC1,也就是TIM4_SR 状态寄存器中位[1] CC1IF,答案我认为就在这一位的功能描述中,如下图(取自《STM32F4xx中文参考手册》P430)
  • 如果大家能够理解中断程序中下面这两句话的作用,就是不断地更新CCR1的值;
 X_TIM_Count = TIM_GetCapture1(TIM3);TIM_SetCompare1(TIM3, (u16)(X_TIM_Count + X_TIM_Pulse));
  • 基于以上几点,我对这段说明的理解是,使用输出比较模式的翻转模式,若ARR不设置为0xFFFF,有可能会因为CCR1大于ARR的值使CC1IF位被意外置1;而设置了ARR=0xFFFF就不会出现这种情况。

原理/思路

  • 步进电机有启动频率这一概念。在启动时有一个最大启动频率,在低于最大启动频率的速度下开始运行,然后通过逐渐加速而达到较高的运行速度,减速亦然。
  • S形加减速算法,也可以通过提前设置速度表或通过输入参数计算出速度表,这两种方式来实现。上一篇梯形加减速算法中我提前设置的速度表,所以这篇我将通过计算的方式得到速度表。
  • S形加减速算法的原型是Sigmoid函数,这部分理论 “pengzhihui2012”的步进电机S型曲线加减速算法与实现.中讲解得比较详细,也感谢这篇文章的作者。我也是主要基于这篇文章做了一个复现。

代码实现

  • X_Step_Motor.h和X_Step_Motor.c,是一个步进电机的控制。
  • X_Step_Motor.h:
#ifndef __X_STEP_MOTOR_H
#define __X_STEP_MOTOR_H#include "sys.h"
#include "math.h"             // exp()
#include <stdio.h>
#include "delay.h"// Motor Parameter
typedef struct{float X_Step;float X_Fre_Min;float X_Fre_Max;float X_Jerk;
}X_SpeedList_TypeDef;
#define     X_TIM3_FREQ                     (84000000 / X_TIM3_Prescaler)       // 2MHz
#define     X_TIM3_Pulse                    (X_TIM3_FREQ / 500)
#define     X_Accel_Step                    100.0f
#define     X_SpeedList_LEN                 ((u8)X_Accel_Step)
#define     X_FREQ_MIN                      500.0f
#define     X_FREQ_MAX                      5000.0f
#define     X_JERK                          4.0f// Motor State
#define     X_ACCEL                         1                       // acceleration
#define     X_COSTT                         2                       // constant
#define     X_DECEL                         3                       // deceleration
#define     X_UNIFM                         4                       // uniform
#define     X_STOP                          0                       // stop#define      TRUE                            1
#define     FALSE                           0// X - TIM3: CH1 - PA6, DIR - PA7
#define     X_TIM3_Prescaler                42
#define     X_TIM3_Period                   0xFFFF
#define     X_TIM3_IRQHandler               TIM3_IRQHandler// Calculate
void X_Calculate_SpeedList(u32 X_PulseNum);// X
void X_GPIO_Init(void);         //GPIO
void X_TIM3_Config(void);       // TIM
void X_PWM_S_Output_Left(void);
void X_PWM_S_Output_Right(void);
void X_Uniform_Output_Left(u32 X_PulseNum);
void X_Uniform_Output_Right(u32 X_PulseNum);
void X_Stop(void);
void X_TIM3_IRQHandler(void);#endif /* __X_STEP_MOTOR_H *//****************************END OF FILE****************************/
  • X_Step_Motor.c:
/**
******************************************************************************
* @file    X_Step_Motor.c
* @author  SieYuan
* @version V1.0
* @date    2021-01-19
* @brief   反转电平输出脉冲,输出固定脉冲数的PWM波STM32F407  84MHz!  高级定时器 168MHz
******************************************************************************
*/
#include "X_Step_Motor.h"
X_SpeedList_TypeDef X_Speed;u32 X_Step_Position = 0;           // 当前位置
u8  X_Motion_Status = 0;           // 0:停止,1:加速,2:匀速,3:减速
float X_Fre_List[X_SpeedList_LEN];          // 频率列表
u16 X_Toggle_Pulse[X_SpeedList_LEN];        // 频率对应的脉冲个数
u32 X_CosTTNum = 0;                            // X 匀速阶段的脉冲个数/******************** X - GPIO *********************/
void X_GPIO_Init(void)
{GPIO_InitTypeDef   GPIO_InitStructure;RCC_APB1PeriphClockCmd( RCC_APB1Periph_TIM3, ENABLE);RCC_AHB1PeriphClockCmd( RCC_AHB1Periph_GPIOA, ENABLE);// GPIOA A6GPIO_PinAFConfig( GPIOA, GPIO_PinSource6, GPIO_AF_TIM3);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;     // TIM3_CH1 - PA6GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;      // 复用GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;      // 推挽复用输出GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;          // 上拉GPIO_Init(GPIOA, &GPIO_InitStructure);// GPIOA A7GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;          // 输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;           GPIO_Init(GPIOA, &GPIO_InitStructure);
}/******************** X - TIM3 *********************/
void X_TIM3_Config(void)
{TIM_TimeBaseInitTypeDef        TIM_TimeBaseStructure;// 时钟频率设置TIM_TimeBaseStructure.TIM_Prescaler = X_TIM3_Prescaler - 1;TIM_TimeBaseStructure.TIM_Period = X_TIM3_Period;TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;TIM_TimeBaseInit( TIM3, &TIM_TimeBaseStructure);TIM_OCInitTypeDef      TIM_OCInitStructure;// 设置工作模式TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;           // 比较输出模式,反转输出TIM_OCInitStructure.TIM_Pulse = X_TIM3_Pulse / 2;         // 让第一个脉冲是500HzTIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;   // 使能比较输出                   TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;           // 输出极性TIM_OC1Init( TIM3, &TIM_OCInitStructure);                // 初始化TIM_OC1PreloadConfig( TIM3, TIM_OCPreload_Disable);       // CH1预装载使能,修改   NVIC_InitTypeDef    NVIC_InitStructure;NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init( &NVIC_InitStructure);
}
/*** 函数功能:速度表计算函数* 输入参数:fre_List[], 各个步的频率数组*         toggle_pulse[], 对应频率的Period*            step[], 变速过程中步数要求, 实际加减速阶段总步数 = 1 + 2 * step = 101*           fre_min - 初始频率,Hz / step/s*           fre_max - 最高频率,Hz / step/s*         jerk, S型的平滑程度,越小越平滑* 说    明:根据速度曲线和加速时间,将数据密集化,即计算每一步的速度值,并存放在内存中。*           这里采用的数学模型是匀变速直线运动,加加速-减加速-匀速-加减速-减减速。
**/
void X_Calculate_SpeedList(u32 X_PulseNum)
{u8 i_x = 0;X_Speed.X_Step = X_Accel_Step;X_Speed.X_Fre_Min = X_FREQ_MIN;X_Speed.X_Fre_Max = X_FREQ_MAX;X_Speed.X_Jerk = X_JERK;float num_x;                                       // 其实是整数float molecule_x;               // 公式分子float denominator_x;     // 公式分母num_x = X_Speed.X_Step / 2;
//  step = step + 1;          // 有一个起始频率,现在用的就是50步。molecule_x = X_Speed.X_Fre_Max - X_Speed.X_Fre_Min;X_CosTTNum = X_PulseNum - (2 * X_Speed.X_Step + 1);for (i_x = 0; i_x < (u8)X_Speed.X_Step; i_x++){denominator_x = 1.0f + (float)exp(-X_Speed.X_Jerk * (i_x - num_x) / num_x);X_Fre_List[i_x] = X_Speed.X_Fre_Min + molecule_x / denominator_x;X_Toggle_Pulse[i_x] = (u16)(X_TIM3_FREQ / X_Fre_List[i_x]);//printf("%d step: frequency: %.2f, pulse:%d.\r\n", i_x, X_Fre_List[i_x], X_Toggle_Pulse[i_x]);}
}void X_PWM_S_Output_Left(void)
{X_Step_Position = 0;X_Motion_Status = X_ACCEL;GPIO_SetBits(GPIOA, GPIO_Pin_7);delay_us(100);GPIO_ResetBits(GPIOA, GPIO_Pin_7);delay_us(125);   //  > 125usX_TIM3_Config();TIM_ClearITPendingBit( TIM3, TIM_IT_CC1);TIM_ITConfig( TIM3, TIM_IT_CC1, ENABLE);TIM_Cmd(TIM3, ENABLE);
}void X_PWM_S_Output_Right(void)
{X_Step_Position = 0;X_Motion_Status = X_ACCEL;GPIO_ResetBits(GPIOA, GPIO_Pin_7);delay_us(100);   GPIO_SetBits(GPIOA, GPIO_Pin_7);delay_us(125);      //  > 125usX_TIM3_Config();TIM_ClearITPendingBit( TIM3, TIM_IT_CC1);TIM_ITConfig( TIM3, TIM_IT_CC1, ENABLE);TIM_Cmd(TIM3, ENABLE);
}void X_Uniform_Output_Left(u32 X_PulseNum)
{X_Step_Position = 0;X_Motion_Status = X_UNIFM;X_CosTTNum = X_PulseNum;GPIO_SetBits(GPIOA, GPIO_Pin_7);delay_us(100);GPIO_ResetBits(GPIOA, GPIO_Pin_7);delay_us(125);   //  > 125usX_TIM3_Config();TIM_ClearITPendingBit( TIM3, TIM_IT_CC1);TIM_ITConfig( TIM3, TIM_IT_CC1, ENABLE);TIM_Cmd(TIM3, ENABLE);
}void X_Uniform_Output_Right(u32 X_PulseNum)
{X_Step_Position = 0;X_Motion_Status = X_UNIFM;X_CosTTNum = X_PulseNum;GPIO_ResetBits(GPIOA, GPIO_Pin_7);delay_us(100);   GPIO_SetBits(GPIOA, GPIO_Pin_7);delay_us(125);     //  > 125usX_TIM3_Config();TIM_ClearITPendingBit( TIM3, TIM_IT_CC1);TIM_ITConfig( TIM3, TIM_IT_CC1, ENABLE);TIM_Cmd(TIM3, ENABLE);
}void X_Stop(void)
{TIM_Cmd(TIM3, DISABLE);        // 关闭定时器TIM_ITConfig( TIM3, TIM_IT_CC1, DISABLE);X_Step_Position = 0;X_Motion_Status = X_STOP;
}/******************** X - IRQ *********************/
void X_TIM3_IRQHandler(void)
{u16 X_TIM_Count = 0;static u8 j_x = 0;volatile static float X_TIM_Pulse = (X_TIM3_Pulse / 2);           // 第一个脉冲 500Hzif (TIM_GetITStatus( TIM3, TIM_IT_CC1) != RESET){TIM_ClearITPendingBit( TIM3, TIM_IT_CC1);       // 清楚中断标志位X_TIM_Count = TIM_GetCapture1(TIM3);j_x++;if (j_x == 2){j_x = 0;if (X_Motion_Status == X_ACCEL){X_Step_Position++;if (X_Step_Position < X_SpeedList_LEN){X_TIM_Pulse = X_Toggle_Pulse[X_Step_Position - 1] / 2;   // -1}else{// 加速阶段第50步,配置好下一个状态X_TIM_Pulse = X_Toggle_Pulse[X_Step_Position - 1] / 2;     if (X_CosTTNum > 0){X_Motion_Status = X_COSTT;}else{X_Motion_Status = X_DECEL;}X_Step_Position = 0;}}else if (X_Motion_Status == X_COSTT){X_Step_Position++;      // 当前将要执行的步数 Step_Position - 1 是已经执行的步数X_TIM_Pulse = X_Toggle_Pulse[X_SpeedList_LEN - 1] / 2;if (X_Step_Position == X_CosTTNum){X_Motion_Status = X_DECEL;X_Step_Position = 0;}}else if (X_Motion_Status == X_DECEL){X_Step_Position++;if (X_Step_Position < (X_SpeedList_LEN + 1)){X_TIM_Pulse = X_Toggle_Pulse[X_SpeedList_LEN - X_Step_Position] / 2;}else{TIM_ITConfig( TIM3, TIM_IT_CC1, DISABLE);TIM_Cmd(TIM3, DISABLE);        // 关闭定时器X_Step_Position = 0;X_Motion_Status = X_STOP;}}else if (X_Motion_Status == X_UNIFM){X_Step_Position++;if (X_Step_Position < X_CosTTNum){X_TIM_Pulse = X_TIM3_Pulse / 2;}else{TIM_ITConfig( TIM3, TIM_IT_CC1, DISABLE);TIM_Cmd(TIM3, DISABLE);     // 关闭定时器X_Step_Position = 0;X_Motion_Status = X_STOP;}}}}TIM_SetCompare1(TIM3, (u16)(X_TIM_Count + X_TIM_Pulse));
}/****************************END OF FILE****************************/
  • main.c
/**
**************************************************************************************
* @file    main.c
* @author  SieYuan
* @version V1.0
* @date    2021-01-19
* @brief   实现  S 形加减速算法
**************************************************************************************
*/
#include    "stm32f4xx.h"
#include    "sys.h"
#include    "led.h"
#include    "key.h"
#include    "exti.h"
#include    "delay.h"
#include    "X_Step_Motor.h"int main(void)
{/* 程序初始化:对【LED】【KEY】【EXIT】【USART】*/LED_Init();KEY_Init();EXTIx_Init();X_GPIO_Init();delay_init(168);X_Calculate_SpeedList(300);X_PWM_S_Output_Left();while(1);
}
/****************************END OF FILE****************************/

实现效果

小结

  • 以上就是S形加减速算法的简单实现。此段代码,加速/减速过程为100步,每个频率走1步。这个部分可以通过调整宏定义进行修改。

  • 我这里使用的是TIM3,如果是使用TIM1\TIM8等高级定时器,注意在配置定时器时添加下面这句,详细可查看 STM32:F103/F407定时器主从模式输出精准脉冲个数.一文中后面的部分。

TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;

  • CSDN下载链接: STM32:F407步进电机S形加减速算法的实现。

STM32:F407步进电机S形加减速算法的实现相关推荐

  1. STM32:F407步进电机梯形加减速算法的实现

    项目中对步进电机运行速度有了新要求,所以尝试实现梯形加减速算法,S形加减速算法. 本文主要实现梯形加减速算法. 更新(22-06-18) 正点原子官6月17日新上线了<电机控制专题例程>, ...

  2. STM32步进电机S型加减速算法

    简单说明一下硬件资源,需要用到STM32两个定时器,TIM1产生PWM脉冲并对脉冲个数计数,TIM2开启定时中断用于算法的实现.采用CubeMX+Hal库配置,这里不做详细介绍,重点介绍S型加减速算法 ...

  3. 步进电机驱动算法——S形加减速算法原理

    目录 步进电机S形加减速简介 七段S形加减速算法原理分析 五段S形加减速算法实现 算法分析 查表法编程实现思路及方法 S曲线加减速流程图 步进电机S形加减速简介 一个物体从起点运动到终点要经历加速.匀 ...

  4. 【电机应用控制】——步进电机控制原理(四相五线/两相四线/细分驱动)驱动器梯形/S形加减速算法直线/圆弧插补

    目录 一.步进电机简介 二.步进电机控制原理 1.四相五线 2.两相四线 3.细分驱动 三.步进电机驱动器 四.梯形加减速算法 五.S形加减速算法 六.直线插补 七.圆弧插补 八.步进电机闭环系统(位 ...

  5. 89c52单片机控制两个步进电机正反转加减速(程序+仿真)

    89c52单片机控制两个步进电机正反转加减速(程序+仿真) 先上图: 源代码: /*********************************************************** ...

  6. S形曲线-斜坡指(S形加减速)

    斜坡指令(S形加减速) 输入值: 输入上限值: 输入下限值: 斜坡加减速时间:ms 2毫秒周期脉冲: 20毫秒周期脉冲: S形曲线加速度: S形曲线加加速度:

  7. 【C语言】STM32控制步进电机——一种S形加减速曲线的推导与实现

    目录 1 前言 2 理论分析 2.1 选择曲线 2.2 计算函数方程 2.3 单位分析 2.4 模拟验证 3 两种代码实现 3.1 速度与时间关系 3.1.1 原理 3.1.2 优点 3.1.3 缺点 ...

  8. 步进电机s型加减速计算工具_步进电机噪音和振动的原因分析及应对策略

    不正确地驱动步进电机很容易导致电机发出"嗡嗡"的噪声和很大的振动. 当驱动步进电机时,如果发现步进电机处于静止状态时,其内部都发出很明显的噪音,有点类似线圈快速变化那种,一般是由于 ...

  9. 步进电机s型加减速计算工具_21个有关伺服电机的问题想当工程师的你一定得知道...

    工业机器人电动伺服系统的一般结构为三个闭环控制,即电流环.速度环和位置环.一般情况下,对于交流伺服驱动器,可通过对其内部功能参数进行人工设定而实现位置控制.速度控制.转矩控制等多种功能. 1.如何正确 ...

最新文章

  1. 观察者模式的经典应用(猫叫 烧开水)
  2. 2019-12-03 有用的学术网站
  3. leetcode 371. 两整数之和(不用算术运算符实现两个数的加法:按位异或原理)
  4. WinForm - 两个窗体之间的方法调用
  5. Chapter 3.GDI/DirectDraw Internal Data Structures
  6. abaqus python二次开发攻略_Abaqus二次开发Python脚本二次开发
  7. hdu5354 Bipartite Graph
  8. C#_基础_部分类partial(十八)
  9. ASP.NET MVC和jQuery系列一:入门篇
  10. shell脚本编程基础(三)
  11. STC 32位8051单片机开发实例教程 二 I/O工作模式及其配置
  12. 决策树 BP神经网络(BPNN) SVM实现iris鸢尾花数据集的分类
  13. 华为铁三角:铁三角模式诞生背景与思考
  14. Web 开发权威指南
  15. B - Distributing Ballot Boxes
  16. win10使用VMware Workstations安装CentOS
  17. SFTP传输文件工具FileZilla下载安装教程
  18. C语言笔记本电脑销售系统课设
  19. 手机用蓝牙键盘好使吗_手机蓝牙键盘有什么用?什么样的人会需要手机键盘?...
  20. Drag and drop拖放框架

热门文章

  1. win server 2012 r2 版本
  2. JAVA类与对象tank_实验四 类与对象
  3. 6.S081 Lab3 page tables 页表
  4. C语言进阶 -- 回调函数以及qsort函数的使用
  5. python心得-字符串列表操作
  6. 2021年土建方向-岗位技能(质量员)多少分及格及土建方向-岗位技能(质量员)模拟考试题库
  7. Linux下安装宋体以及微软雅黑字体-转PDF后不显示中文,以及中文乱码
  8. nvm的下载、安装、卸载踩坑
  9. 2023基于微信小程序的大学生社团活动报名管理系统(SSM+mysql)-JAVA.VUE(论文+开题报告+运行)
  10. 支付宝小程序中实现搜索输入提示关键字高亮显示