推广 热搜:
 |  行业设备  |  机械制造  |  机器视觉  |  机械自动化  |  仪器仪表  |  工业机器人  |  编程开发  |  嵌入式  |  电子技术  |  工控自动化  |  电工电气 频道

STM32-步进电机S型加减速控制

   日期:2019-09-08     浏览:7    评论:0    
核心提示:基于STM32的步进电机S型加减速控制算法STM32简介STM32代表ARM Cortex-M内核的32位微控制器。专为要求高性能、低成本、低功耗的嵌入式应用专门设计的:STM32系列的内核主要有
基于STM32的步进电机S型加减速控制算法
 
STM32简介 
STM32代表ARM Cortex-M内核的32位微控制器。专为要求高性能、低成本、低功耗的嵌入式应用专门设计的: 
STM32系列的内核主要有:Cortex-M0、Cortex-M0+、Cortex-M3、Cortex-M4、Cortex-M7 
STM32内部资源: 
1.GPIO: 
2.外部中断:STM32的任意一个GPIO均可设置为外部中断源 
3.DMA控制器:用于在外设与存储器之间以及存储器与存储器之间提供高速数据传输。可以在无需任何 CPU 操作的情况下通过 DMA 快速移动数据。这样节省的 CPU 资源可 供其它操作使用。 
4.模拟/数字转换(ADC):可配置 12 位、10 位、8 位或 6 位分辨率,它具有多达 19 个复用通道,可测量来自 16 个外部 源、两个内部源和 VBAT 通道的信号 
5.数字/模拟转换(DAC):DAC 模块是 12 位电压输出数模转换器 
(ADC/DAC分辨率:能够反映模拟量变化的最小单位。例:STM32 ADC的满量程为3.3V,在12位ADC转换中能识别的最小电压变化值为:3.3/2^12=3.3/4096=0.000244V) 
6.定时器:高级定时器(TIM1,TIM8)、通用定时器(TIM2~5)、基本定时器(TIM6/TIM7)
 
图1:STM32定时器简介
 
SDIO接口: 
内存扩展USB全速接口: 
bxCAN控制器局域网:1986 年德国电气商博世公司开发出面向汽车的CAN 通信协议,并被广泛地应用于工业自动化、船舶、医疗设备、工业设备等方面 。 
支持最大传输波特率1Mbps,当波特率设置为125kbps以下时,可靠传输距离可达1千米以上,总线上可挂载若干个CAN通信外设; 
图2:车辆中所用到的CAN通信
 
串行外设接口(SPI): 
I2C接口: 
通用同步异步收发器(USART): 
以太网(ETH):物联网
 
步进电机: 
步进电机特点
 
在步进电机的控制过程中由于负载的变化和运行速度的影响常常会产生失步和过冲现象致使控制精度下降。失步和过冲现象分别出现在步进电机的启动和停止的时候。其中失步是由于同步力矩无法使转子速度跟随定子磁场的旋转速度,从而引起失步;而在控制脉冲结束时,转子在步进过程中获得过多的能量,平均速度会高于定子磁场的平均转速,使得步进电机输出转矩偏大,产生了过冲现象。 
为了克服失步和过冲现象,应采用与电机控制相适应的控制算法即在启动和停止时实行加减速控制,其实质是在速度变化过程中控制发送脉冲的频率实现速度的加减速。通常加减速算法主要有梯形曲线、指数曲线和S型曲线。 
(1)梯形运行曲线 
对于梯形曲线来说,电机启动后做匀加速运动,达到预定速度后,电机匀速转动,然后做匀减速运动至停止。其速度和加速度曲线如图3所示。其速度的公式为: 
 
 
图4-8梯形曲线速度和加速度曲线 
因为脉冲频率控制步进电机的转速,其初始速度V0对应步进电机的启动频率,Vm为匀速运行速度,在加速阶段t1内,经历了n1个脉冲,通过已知Vm-V0可以求出a1即加速阶段的加速度。其中加速阶段的每个脉冲周期为Tx,最小的为Tm,最大的为T0,匀加速过程中每次脉冲周期减少时间即脉冲周期变化量为 。其中转速的计算可由步距角比时间获得: 
 
 
通过速度之差可得出加速阶段的 计算公式: 
减速阶段的计算公式与加速阶段的类似。得到 之后在脉冲循环函数中每次对定时器的ARR增加或减少相应的数值便可获得匀加速和匀减速效果。 
梯形曲线其特点是算法简便,占用时少、响应快、效率高,实现方便[48]。但匀加速和匀减速阶段不符合步进电机速度变化规律,在变速和匀速转折点不能平滑过渡,这将影响电机的运行效率和使用寿命,所以这种算法主要应用在对升降速过程要求不高的场合。 
(2)指数运行曲线 
作为数控系统中较常见的加减速模型,指数运行曲线不同于梯形曲线在于其加速过程是按指数规律变化,加速度变化规律函数与速度变化规律函数互为反函数。运动规律如图7。 
 
图7 指数曲线速度和加速度曲线图
 
其速度公式为: 
式中vm为指定进给速度,t为加减速时间, 为加速度时间系数(与电机自身参数有关)。理论上加速阶段步进电机的运行频率为: 
其中fm为最高脉冲频率即匀速时候的控制频率。但在实际控制中,如果直接对频率公式程序编写会造成由于参数过多计算量过大引起的CPU负荷过大,从而导致程序运行出错。所以通常情况会进行离散逼近的方式来完成曲线控制。将加速段的时间分为N段,则每次加速度改变的时间为: 
可以得到每段的控制频率为: 
则每段的运行步数为: 
QQ浏览器截图20190908114900
这种方法均匀的离散了加速度的变化,但在算法实现时的加速度时间分段是不均匀的,所以和设计的曲线合适有一定的出入。指数曲线克服了梯形加减速的速度不平稳问题,运动精度得到了提高,但初始加速度大,容易引起机械部件的冲击,在加减速的起点仍然存在加减速突变,限制了加速度的提高。 
(3)S型曲线 
S型曲线是相对较新的控制算法,常用来解决控制加速度的突变现象,由于加减速过程的速度曲线像S型得名。S型曲线并不是一种固定的算法,可分为7个阶段,加加速段、匀加速段、减加速段、匀速段、加减速段、匀减速段和减减速段,其中控制匀速阶段和匀加速阶段的有无有可分为4段型、5段型和6段型,具体如图10所示,通过调整不同阶段的参数得到不同性能的加减速特性,常见的S曲线有抛物线型和三角函数型,因此应用灵活。S型曲线的核心思想是让加速度不产生突变,其加减速平稳,柔性快速,是一种综合性能比较突出的加减速模型。因此本课题的步进电机控制采用了S型曲线控制。 
 
图10 S型曲线速度、加速度和加加速度曲线图 
其速度公式为 
 
式(13)描述了步进电机整个的速度变化过程。虽然S曲线在加减速控制方面相较梯形和指数型有很大的提高,但是由于涉及阶段较多,实现较为复杂,因此需要对控制曲线进行一定的离散拟合优化,从而更好的实现控制要求。 
基于STM32的步进电机S型加减速实现
 
问题:1.怎么样利用STM32输出脉冲信号?(1.定时器PWM输出;2.GPIO+高精度延时函数) 
2.怎么样设置脉冲频率?(预分频系数TIMx->PSC、计数重载值TIMx-> ARR) 
3.怎么样输出定量的脉冲信号?(1.定时器外部计数(高速);2.外部中断计数(低速))
 
步进电机闭环控制框图 
 
步进电机通常情况是不带编码器的,哪来的信号反馈? 
利用步进电机的特性(在非超载的情况下,电机的转速、停止的位置只取决于脉冲信号的频率和脉冲数,而不受负载变化的影响),采集控制器输出的脉冲作为反馈信号,进行无传感器的“伪”闭环控制,控制框图如下:
 
STM32实现方案1:利用STM32定时器 (高级或通用定时器)输出PWM控制步进电机,开启另一个定时器(高级或通用定时器 )为外部计数模式进行输出脉冲采集,设置以细分数为计数溢出中断,在中断里改变PWM输出定时器的输出频率。 
方案2:利用STM32高级定时器输出PWM控制步进电机,同时开启定时器溢出中断,在高级定时器中的重复计数寄存器RCR(寄存器为8位寄存器,计数范围0~255),并初始化该寄存器值为细分数,在溢出中断中改变PWM输出频率,实现步进电机速度调节。提示:如不开启此寄存器,定时器每输出一个脉冲触发一次中断,而PWM输出频率通常很高,控制器将不停的开启中断/关闭中断消耗CPU。
 
现在回到S型控制算法: 
在S型曲线加减速控制算法中,涉及到的参数有:1.最大速度;2.最大加速度;3.起跳转速;4.加加速时间(加加速脉冲数);5.匀加速时间(匀加速脉冲数),6.加速总时间(总脉冲数)等,这么多参数,怎么样确定呢?
 
理论计算+经验
 
1.最大速度:步进电机输出转矩随转速的增加呈减少趋势,计算最大负载,为保证步进电机运行时不失步,步进电机输出转矩以大于最大负载的1.5~2倍 
2.最大加速度: 
3.起跳转速:步进电机的参数手册里会提及 
4~6 .加加速时间不应大于总加速时间的1/4;总加速时间0~1.5s 
当这些参数确定好后,代入公式(1-1)便可得到步进电机运行的个阶段速度曲线,同时为了简化算法,减少MCU计算量,采用一步一插值的方法获取步进电机每一步运行的频率(采用细分控制时每一步运行在同一频率下),并将插取值换算成定时器重装载寄存器(TIMx-ARR)的值做成数组保存在MCU内存中,这样控制器只需要按顺序在数组里读取值进行定时器计数值重装载就行了。下面附上步进电机S型加减速代码:
 
#include "spwm.h"
#include "motor.h"
#include "eesv3.h"
#include
#include "can.h"
#include "usart2.h"
 
#if SYSTEM_SUPPORT_OS
#include "includes.h"                   //os ʹÓà    
#endif
 
uint16_t  T8_integer_num;     //ÔÈËٽ׶ÎÂö³åÊýµÄ255µÄÕûÊý±¶
uint8_t   T8_remainder_num; //ÔÈËٽ׶ÎÂö³åÊýµÄ255µÄÕûÊý±¶ÓàÊý
uint8_t TIM8_STATUS=0;          //TIM3¶¨Ê±Æ÷¹¤×÷״̬±êÖ¾£¨0£º¿ÕÏУ»1£ºÃ¦£©
uint8_t T8Motor_Code=0;         //µç»ú´úºÅ£¬ÉèÖóÉÈ«¾Ö±äÁ¿
uint8_t T8RunModel=0;           //µÚ¶þ×éµç»úÔËÐбê־λ
 
uint16_t PREQ_TABBUR[31]={6400,6502,6810,7322,8038,8960,9424,10448,11472,12496,13520,14544,15568,16592,
//  17616,18640,19664,20688,21712,22736,23760,24784,25808,26832,27856,28880,30362,31078,31590,31898,32000}; 
 
 
static void TIM8_PWM_RCR_Config(u16 arr,u16 psc)
{                            
    GPIO_InitTypeDef GPIO_InitStructure;
    TIM_TimebaseInitTypeDef  TIM_TimebaseStructure;
    TIM_OCInitTypeDef  TIM_OCInitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
 
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM8, ENABLE); //TIM8ʱÖÓʹÄÜ
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC , ENABLE);  //ʹÄÜGPIOCÍâÉèʱÖÓʹÄÜ                                                                         
 
  //ÉèÖøÃÒý½ÅΪ¸´ÓÃÊä³ö¹¦ÄÜ,Êä³öTIM8 CH2µÄPWMÂö³å²¨ÐÎ
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9; //TIM8_CH2
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //¸´ÓÃÍÆÍìÊä³ö
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOC, &GPIO_InitStructure);
 
    TIM_TimebaseStructInit(&TIM_TimebaseStructure);
 
    TIM_TimebaseStructure.TIM_Period = arr; //ÉèÖÃÔÚÏÂÒ»¸ö¸üÐÂʼþ×°Èë»î¶¯µÄ×Ô¶¯ÖØ×°ÔؼĴæÆ÷ÖÜÆÚµÄÖµ     
    TIM_TimebaseStructure.TIM_Prescaler =psc; //ÉèÖÃÓÃÀ´×÷ΪTIMxʱÖÓƵÂʳýÊýµÄÔ¤·ÖƵֵ  
    TIM_TimebaseStructure.TIM_ClockDivision = 0; //ÉèÖÃʱÖÓ·Ö¸î:TDTS = Tck_tim
    TIM_TimebaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIMÏòÉϼÆÊýģʽ
    TIM_TimebaseInit(TIM8, &TIM_TimebaseStructure); //¸ù¾ÝTIM_TimebaseInitStructÖÐÖ¸¶¨µÄ²ÎÊý³õʼ»¯TIMxµÄʱ¼ä»ùÊýµ¥Î»
    TIM_ClearITPendingBit(TIM8,TIM_IT_Update);
 
    TIM_UpdateRequestConfig(TIM8,TIM_UpdateSource_Regular);
//  TIM_SelectonePulseMode(TIM8,TIM_OPMode_Single);
 
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //Ñ¡Ôñ¶¨Ê±Æ÷ģʽ:TIMÂö³å¿í¶Èµ÷ÖÆģʽ2
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //±È½ÏÊä³ö2ʹÄÜ
    TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Disable;
    TIM_OCInitStructure.TIM_Pulse = arr>>1; //ÉèÖôý×°È벶»ñ±È½Ï¼Ä´æÆ÷µÄÂö³åÖµ
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //Êä³ö¼«ÐÔ:TIMÊä³ö±È½Ï¼«ÐÔ¸ß
 
    TIM_OC1Init(TIM8, &TIM_OCInitStructure);                //¸ù¾ÝTIM_OCInitStructÖÐÖ¸¶¨µÄ²ÎÊý³õʼ»¯ÍâÉèTIMx
    TIM_OC1PreloadConfig(TIM8, TIM_OCPreload_Enable); //ʹÄÜTIMxÔÚCCR1ÉϵÄԤװÔؼĴæÆ÷
    TIM_OC2Init(TIM8, &TIM_OCInitStructure); 
    TIM_OC2PreloadConfig(TIM8, TIM_OCPreload_Enable); //ʹÄÜTIMxÔÚCCR2ÉϵÄԤװÔؼĴæÆ÷
    TIM_OC3Init(TIM8, &TIM_OCInitStructure);                
    TIM_OC3PreloadConfig(TIM8, TIM_OCPreload_Enable); //ʹÄÜTIMxÔÚCCR3ÉϵÄԤװÔؼĴæÆ÷
    TIM_OC4Init(TIM8, &TIM_OCInitStructure);                
    TIM_OC4PreloadConfig(TIM8, TIM_OCPreload_Enable); //ʹÄÜTIMxÔÚCCR4ÉϵÄԤװÔؼĴæÆ÷
 
    TIM_ARRPreloadConfig(TIM8, ENABLE); //ʹÄÜTIMxÔÚARRÉϵÄԤװÔؼĴæÆ÷
 
    TIM_ITConfig(TIM8, TIM_IT_Update ,ENABLE);  //TIM8   Ê¹ÄÜ»òÕßʧÄÜÖ¸¶¨µÄTIMÖжÏ
 
    NVIC_InitStructure.NVIC_IRQChannel = TIM8_UP_IRQn;  //TIM8ÖжÏ
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;  //ÏÈÕ¼ÓÅÏȼ¶1¼¶
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;  //´ÓÓÅÏȼ¶1¼¶
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQͨµÀ±»Ê¹ÄÜ
    NVIC_Init(&NVIC_InitStructure);  //¸ù¾ÝNVIC_InitStructÖÐÖ¸¶¨µÄ²ÎÊý³õʼ»¯ÍâÉèNVIC¼Ä´æÆ÷
 
    TIM_ClearITPendingBit(TIM8, TIM_IT_Update);  //Çå³ýTIMxµÄÖжϴý´¦Àíλ:TIM ÖжÏÔ´
    TIM_Cmd(TIM8, ENABLE);  //ʹÄÜTIM8                                    
}
 
void TIM8_PWM_OUT_Init(uint16_t arr,uint16_t psc)
{
    TIM8_PWM_RCR_Config(arr, psc);
    TIM_SetCompare1(TIM8,0); //ÉèÖÃͨµÀ1µÄÕ¼¿Õ±È50%
    TIM_SetCompare2(TIM8,0); //ÉèÖÃͨµÀ2µÄÕ¼¿Õ±È50%
    TIM_SetCompare3(TIM8,0); //ÉèÖÃͨµÀ3µÄÕ¼¿Õ±È50%
    TIM_SetCompare4(TIM8,0); //ÉèÖÃͨµÀ4µÄÕ¼¿Õ±È50%
}
 
void TIM8_Startup(uint8_t Channel,uint32_t Frequency)
{
    u16 temp_arr=1000000/Frequency-1; 
    TIM_SetAutoreload(TIM8,temp_arr);// 
    switch(Channel)
    {
        case 1:
                    TIM_SetCompare1(TIM8,temp_arr>>1); //ÉèÖÃͨµÀ1µÄÕ¼¿Õ±È50%
            break;
        case 2:
                    TIM_SetCompare2(TIM8,temp_arr>>1); //ÉèÖÃͨµÀ2µÄÕ¼¿Õ±È50%
            break;
        case 3:
                    TIM_SetCompare3(TIM8,temp_arr>>1); //ÉèÖÃͨµÀ3µÄÕ¼¿Õ±È50%       
            break;
        case 4: 
                    TIM_SetCompare4(TIM8,temp_arr>>1); //ÉèÖÃͨµÀ4µÄÕ¼¿Õ±È50%   
            break;
        default: break;
    }   
    TIM_SetCounter(TIM8,0);//
    TIM_CtrlPWMOutputs(TIM8,ENABLE);    //MOE Ö÷Êä³öʹÄÜ£¨¸ß¼¶¶¨Ê±Æ÷ʱ±ØÒªµÄ»·½Ú£©
    TIM_Cmd(TIM8, ENABLE); //
}
 
void TIM8_UP_IRQHandler(void)
{
    static uint8_t Accel_num=0,Decel_num=STEP_NUMX;
    static uint32_t totall=0;
    static CanBus Can1; 
 
#ifdef SYSTEM_SUPPORT_OS        
    OSIntEnter();    
#endif
    if(TIM_GetITStatus(TIM8,TIM_FLAG_Update)!=RESET)
    {
        TIM_ClearITPendingBit(TIM8,TIM_FLAG_Update);
        if(TIM8_STATUS==1)
        {
            if(Accel_num
 
打赏
 
更多>同类 嵌入式
0相关评论

推荐图文
推荐 嵌入式
点击排行

网站首页  |  关于我们  |  联系方式  |  使用协议  |  版权隐私  |  网站地图  |  排名推广  |  广告服务  |  积分换礼  |  网站留言  |  RSS订阅  |  违规举报