找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 2209|回复: 37
收起左侧

求多任务系统中步进电机的平稳运行方案

  [复制链接]
ID:997026 发表于 2022-8-3 17:37 | 显示全部楼层 |阅读模式


调了好几天都没搞定,发现步进电机驱动起来很简单,但是要想电机转动平稳太难了,尤其是在多任务的系统中。
我的系统现在有多个任务,就是在while(1)循环中有好几个函数需要执行,步进电机驱动是其中一个,现在如果把步进电机测试函数
单独放在while(1)中,如下

int main(void)
{

while(1)
{
    motor_test(); //步进电机驱动
}

}

那么步进电机运转很丝滑,很平稳,我通过多次调试找到了延时设置为1500us时,电机运转最平稳,当然这只限于while(1)中只有这一个函数时,

如果我同时运行其他任务,如下

int main(void)
{

while(1)
{
    motor_test(); //步进电机驱动
    read_sensor();
    status_update();

}

}


void  motor_test()
{
       
                PAH; PBL;PCL;PDL;
SysTick_delay_us(1500);       
                PAH; PBH;PCL;PDL;
SysTick_delay_us(1500);       
                PAL; PBH;PCL;PDL;
        SysTick_delay_us(1500);
                PAL; PBH;PCH;PDL;
        SysTick_delay_us(1500);
                PAL; PBL;PCH;PDL;       
SysTick_delay_us(1500);       
                PAL; PBL;PCH;PDH;
        SysTick_delay_us(1500);
                PAL; PBL;PCL;PDH;       
        SysTick_delay_us(1500);
                PAH; PBL;PCL;PDH;
        SysTick_delay_us(1500);
}


那么,电机会转起来,但是会产生震动,怎么调整延时值都没用,1500上下的延时我都试过了,一样震动,可能有人会说,用定时器中断,我当然试过,而且调试了很长时间,一样的结果也是震动,我知道是其他程序影响了电机运转,但不至于对时间要求这么精确把?其他的程序执行会分掉时间,但不至于会影响到电机这么严重吧?
我甚至把这两个函数read_sensor();和status_update();用时间片轮询的方法写了一遍,就是给他们分配更长的执行时间,让步进电机执行时间尽可能短,也还是不行,还是震动。

真的没有思路了,大家有什么好的思路吗?可以解决震动问题

回复

使用道具 举报

ID:161164 发表于 2022-8-3 22:54 | 显示全部楼层
定时器中断内是怎么写的?
直接放motor_test()进中断?
回复

使用道具 举报

ID:1023753 发表于 2022-8-4 08:05 | 显示全部楼层
定时器中断中来分别执行步进电机每一步
回复

使用道具 举报

ID:712493 发表于 2022-8-4 08:18 | 显示全部楼层
lkc8210 发表于 2022-8-3 22:54
定时器中断内是怎么写的?
直接放motor_test()进中断?

那应该是吧!哈哈 楼主土豪  直接一个一个1500us延时 整个驱动下来消耗多少时间,确实只能一个函数运行,放那都一样
回复

使用道具 举报

ID:844772 发表于 2022-8-4 10:39 | 显示全部楼层
你的问题是每个八拍间的延迟,关键看其他任务占时是多少,如果在1.25毫秒内,看剩多少在motor-test()最后那句找齐。如果其他任务时间不确定,就通过中断计时,改步进电机程序,判断中断计数执行相应步
回复

使用道具 举报

ID:844772 发表于 2022-8-4 10:44 | 显示全部楼层
刚才回答没仔细看你的问题,我觉得你不是延时的问题是电源的问题,一般步进电机对时间要求没那么精确,震动是电源功率不足或者转速太快。
回复

使用道具 举报

ID:213173 发表于 2022-8-4 12:56 | 显示全部楼层
int main(void)
{
        while(1)
        {
                motor_test(); //步进电机驱动
                read_sensor();
                status_update();
//                SysTick_delay_us(xxxx);//选用
        }
}

void  motor_test()
{
        static unsigned char i=0;
        switch(i)
        {
                case 0: PAH;PBL;PCL;PDL; break;
                case 1: PAH;PBH;PCL;PDL; break;
                case 2: PAL;PBH;PCL;PDL; break;
                case 3: PAL;PBH;PCH;PDL; break;
                case 4: PAL;PBL;PCH;PDL; break;
                case 5: PAL;PBL;PCH;PDH; break;
                case 6: PAL;PBL;PCL;PDH; break;
                case 7: PAH;PBL;PCL;PDH; break;
        }
        i=++i%8;
}
回复

使用道具 举报

ID:883242 发表于 2022-8-4 13:36 | 显示全部楼层
放在定时中断里面,难道你不会用中断?
回复

使用道具 举报

ID:879348 发表于 2022-8-4 13:47 | 显示全部楼层
步进驱动放到定时中断
回复

使用道具 举报

ID:526108 发表于 2022-8-4 14:20 | 显示全部楼层
步进电机直接放定时器中驱动
回复

使用道具 举报

ID:276685 发表于 2022-8-4 15:57 | 显示全部楼层
回复

使用道具 举报

ID:997026 发表于 2022-8-4 20:59 | 显示全部楼层
lkc8210 发表于 2022-8-3 22:54
定时器中断内是怎么写的?
直接放motor_test()进中断?

现在问题分两种情况:
一种是如上面的1500us的延时写法,while(1)循环里只放motor_tet(), 电机运转丝滑很平稳,如果同时放其他函数,电机就会发生震动,无论怎么调延时都没用。

另一种情况是,用定时器中断写法,定时器中断写法问题更严重,无论while(1)循环里是只放motor_tet(),还是同时放其他函数,都会发生震动,感觉转动很不连续。我怀疑是不是我的定时器中断程序写错了?或者我的中断开多了?因为我程序里总共开了三个中断,一个20ms, 一个1ms,再就是systick中断是100us进一次,我是把电机控制放在systick中断里,如下
void SysTick_IrqHandler(void) //配置滴答时钟每100us进一次中断
{
       if(motor_run_flag==1)
        {
                    motor_run_cnt++;
        }
       
}

我没有把motor_test()直接放在中断里,而是定义了一个计数器在中断计数,电机运行放在主程序,根据中断计数,到一定时间就翻转IO,这样做是为了方便控制,可以设置电机速度,如下
void motor_test(uint16_t speed) //四相八拍
{
                motor_run_flag=1;
                if(motor_run_cnt<speed) //speed以1500us为例,speed=15,100us进一次中断
                {
                        PAH;PBH;PCL;PDL; //A
                }
                else if(motor_run_cnt<speed*2) //3000
                {
                        PAL;PBH;PCH;PDL; //AB
                }
                else if(motor_run_cnt<speed*3) //4500
                {
                        PAL;PBL;PCH;PDH; //B
                }
                else if(motor_run_cnt<speed*4) //6000
                {
                        PAH;PBL;PCL;PDH; //BC
                }
                else if(motor_run_cnt<speed*5) //7500
                {
                        PAL;PBL;PCH;PDL; //C
                }
                else if(motor_run_cnt<speed*6) //9000
                {
                        PAL;PBL;PCH;PDH; //CD
                }
                else if(motor_run_cnt<speed*7) //10500
                {
                        PAH;PBL;PCL;PDH; //D
                }
                else if(motor_run_cnt<speed*8) //12000
                {
                        PAH;PBL;PCL;PDH; //DA
                }
                else
                {
                        angle_cnt++;  //运行完上面八拍自增一次,记录运行了几个八拍
                        if(angle_cnt>= 4096) //当运行到4096个八拍时停止电机
                        {
                                motor_run_cnt=0xF4240; //停止计数
                                angle_cnt= 4096;
                                PAL;PBL;PCL;PDL; //停止
                               
                        }
                        else //未计数到4096时循环运行
                        {
                                motor_run_cnt =0;
                        }
                                     
                }
}

以上的程序还不完善,只是为了测试,第一次做电机不知道写的对不对,但感觉还是有点问题,因为上面的 程序直接放在while(1)中电机无论调到哪个速度都会震动始终不能平稳运行,更别说while(1)中还有其他程序,所以感觉这个代码肯定是有问题的。
回复

使用道具 举报

ID:997026 发表于 2022-8-4 21:03 | 显示全部楼层
glinfei 发表于 2022-8-4 10:44
刚才回答没仔细看你的问题,我觉得你不是延时的问题是电源的问题,一般步进电机对时间要求没那么精确,震动 ...

问题是我现在用1500us的延时,在while(1)循环中只放motor_test()这个函数,电机没有任何震动,运转非常丝滑,连声音都几乎听不到,非常细腻,但是一旦while(1)中放了其他函数,电机就开始震动,无论你设置什么延时都不能解决。我步进电机是12V 24BJY48减速电机,用的是ULN2003驱动,外部电源的最大输出是2A,应该带的动吧?单片机IO是3.3V电平,可以驱动ULN2003吗?但是如刚才说,在while(1)循环中只放motor_test()这个函数,电机没有任何震动,运转非常丝滑。
回复

使用道具 举报

ID:997026 发表于 2022-8-4 21:11 | 显示全部楼层
fj51hei 发表于 2022-8-4 08:18
那应该是吧!哈哈 楼主土豪  直接一个一个1500us延时 整个驱动下来消耗多少时间,确实只能一个函数运行, ...

你错了,这样写是为了测试我的电机是不是有问题,刚开始我是定时器中断写法,但是怎么都不能解决震动,我用这种延时写法来测试到底是程序问题还是电机问题,并不是我的项目中要这样写。我的单片机是M4核,200M主频,192K RAM,我测试过,就算我用这种1500us延时放在while(1)循环一直运行,都丝毫不会影响我其他程序的运行,现在的问题是电机运行不连续有震动,我认为是程序需要优化,但肯定不会用这种延时写法,只是为了测试用
回复

使用道具 举报

ID:997026 发表于 2022-8-4 21:30 | 显示全部楼层
wulin 发表于 2022-8-4 12:56
int main(void)
{
        while(1)

感谢提供代码,我试试
回复

使用道具 举报

ID:844772 发表于 2022-8-5 08:42 | 显示全部楼层
hxdby 发表于 2022-8-4 21:03
问题是我现在用1500us的延时,在while(1)循环中只放motor_test()这个函数,电机没有任何震动,运转非常丝 ...

我都是用5v驱动ULN2003,3.3V单独驱动它也没问题,但你又接了其他传感器,就不一定行了。步进电机本来为了控制动作,就是根据任务调整延时的,你的问题时出现震动,这不是延时造成的。
回复

使用道具 举报

ID:161164 发表于 2022-8-5 09:54 | 显示全部楼层
hxdby 发表于 2022-8-4 20:59
现在问题分两种情况:
一种是如上面的1500us的延时写法,while(1)循环里只放motor_tet(), 电机运转丝滑 ...

把7楼的motor_test()放到1500us中断里运行看看
回复

使用道具 举报

ID:997026 发表于 2022-8-5 10:05 | 显示全部楼层
lkc8210 发表于 2022-8-5 09:54
把7楼的motor_test()放到1500us中断里运行看看

好的,我也正想这样试试
回复

使用道具 举报

ID:997026 发表于 2022-8-5 10:09 | 显示全部楼层
glinfei 发表于 2022-8-5 08:42
我都是用5v驱动ULN2003,3.3V单独驱动它也没问题,但你又接了其他传感器,就不一定行了。步进电机本来为 ...

我也感觉不是延时问题,总感觉是定时中断被什么打断了,因为我系统里总共开了三个中断,是不是优先级设置的不够不打断了?因为现在电机的现象就是震动,运转不连续,而且扭矩很小。

下一步我准备按照Ikc8210建议,用wulin的代码,直接放到1500us的中断里试试,同时把这个中断优先级设置到最高,不能被抢占,不能被打断。
回复

使用道具 举报

ID:123289 发表于 2022-8-5 14:10 | 显示全部楼层
其实很简单,道理:牛顿定律,F=M*a。
你给电机通电,就是给电机加力F,这个F是有极限的(取决于电机的功率)。
电机有负载,就是M,M与负载的大小相关,所以加速度a不是恒量。
平稳:当a恒定时,给你的感觉就平稳了。
电机从静止到有速度,或从速度1,过渡到速度2是需要时间的,在F、M的限制下,过渡过程需要的时间T就需要关注了,这也是你唯一可智能控制的参数(提速度的时间)。
你的极限提速时间(从频率1升(降)到频率2)是T,如果变频太快,就会感觉到有“振动”,其实是F<M*a,拖不动,电机失步了。
就简单的升速控制T方案是延时DT,明白上述道理后就会知道DT是个变量,你提出1500us,只是在某一个点的工况下OK,换个工况就不一定OK了。
搞定步进电机,也是需要物理知识的,更需要有智慧找出控制方案。

回复

使用道具 举报

ID:997026 发表于 2022-8-5 16:31 | 显示全部楼层
fbn20050523 发表于 2022-8-4 15:57
看我发的帖子http://www.51hei.com/bbs/dpj-220045-1.html

你这个比较高级,我这个控制比较简单,主要就是控制一个流量阀的开合,来控制气体的流量,匀速运动就可以了,也不需要很快的转速,主要是位置比较重要。你这个代码主要是用于哪方面的控制?
回复

使用道具 举报

ID:690831 发表于 2022-8-6 00:40 来自手机 | 显示全部楼层
你的步进电机是四相五线的,还是两相四线的
回复

使用道具 举报

ID:690831 发表于 2022-8-6 00:44 来自手机 | 显示全部楼层
震动,是原地左右摆动,通常是驱动时序错了。
回复

使用道具 举报

ID:883242 发表于 2022-8-6 13:50 | 显示全部楼层
iamluckman 发表于 2022-8-6 00:40
你的步进电机是四相五线的,还是两相四线的

都说了是ULN2003驱动,你还不知道几根线?
回复

使用道具 举报

ID:997026 发表于 2022-8-6 21:52 | 显示全部楼层
iamluckman 发表于 2022-8-6 00:40
你的步进电机是四相五线的,还是两相四线的

24BJY48,步进减速电机,四相八拍驱动方式,四相五线
回复

使用道具 举报

ID:997026 发表于 2022-8-6 21:54 | 显示全部楼层
iamluckman 发表于 2022-8-6 00:44
震动,是原地左右摆动,通常是驱动时序错了。

不是原地左右摆动,是可以按照程序运转一圈或几圈,就是运行的同时有震动
回复

使用道具 举报

ID:690831 发表于 2022-8-9 02:37 | 显示全部楼层
hxdby 发表于 2022-8-6 21:54
不是原地左右摆动,是可以按照程序运转一圈或几圈,就是运行的同时有震动

MCU stc90c52rc
回复

使用道具 举报

ID:690831 发表于 2022-8-9 02:41 | 显示全部楼层
hxdby 发表于 2022-8-6 21:54
不是原地左右摆动,是可以按照程序运转一圈或几圈,就是运行的同时有震动

你试试这个会不会顿挫感
mcu stc90c52RC
晶振 11.0592
电机 28BYJ-48

#include "STC89.h"
//#include "main.h"
#include "intrins.h"

typedef   unsigned char uchar;
typedef   unsigned int uint;
typedef  uchar u8;
typedef  uint u16;
typedef  long int u32;
#define nop         _nop_()

#define FOSC 11059200L      //晶振
#define BAUD 9600           //波特率
#define Forward 1
#define Backward 0

#define   M1_A P10
#define   M1_B P11
#define   M1_C P12
#define   M1_D P13

#define   Delay_2003  nop

void Delay100ms()                //@11.0592MHz
{
    unsigned char i, j, k;
    _nop_();
    _nop_();
    i = 5;
    j = 52;
    k = 195;
    do
    {
        do
        {
            while ( --k );
        }
        while ( --j );
    }
    while ( --i );
}
//-----------------------------------------------
//direction 正反转
//run_part  运行步数
u8 M1_run ( u8 direction,u16 run_part  ) ////减速比 1/64
{
//    static        u16 f=2000;
    static u8 M1_part=1;
    static u16 part=0;
    static bit f=0;
    static u16 _run_part;
    if ( f==0 ) //是一个新数据
    {
        _run_part=run_part;//新步数载入缓存
        part=0;//拍数从零开始
        f=1;//
    }
    if ( part<_run_part )
    {
        part++;//记录已运行步数
        if        ( direction==Forward ) //正转
        {
            if ( M1_part ==8 )
            {
                M1_part=0;
            }
            M1_part++;
        }
        else if ( direction==Backward ) //反转
        {
            if ( M1_part==1 )
            {
                M1_part=9;
            }
            M1_part--;
        }
        switch ( M1_part )
        {
        case ( 1 ) :
            M1_A =1;
            M1_B =0;
            M1_C =0;
            M1_D =0;
            Delay_2003;
            break;
        case ( 2 ) :
            M1_A =1;
            M1_B =1;
            M1_C =0;
            M1_D =0;
            Delay_2003;
            break;
        case ( 3 ) :
            M1_A =0;
            M1_B =1;
            M1_C =0;
            M1_D =0;
            Delay_2003;
            break;
        case ( 4 ) :
            M1_A =0;
            M1_B =1;
            M1_C =1;
            M1_D =0;
            Delay_2003;
            break;
        case ( 5 ) :
            M1_A =0;
            M1_B =0;
            M1_C =1;
            M1_D =0;
            Delay_2003;
            break;
        case ( 6 ) :
            M1_A =0;
            M1_B =0;
            M1_C =1;
            M1_D =1;
            Delay_2003;
            break;
        case ( 7 ) :
            M1_A =0;
            M1_B =0;
            M1_C =0;
            M1_D =1;
            Delay_2003;
            break;
        case ( 8 ) :
            M1_A =1;
            M1_B =0;
            M1_C =0;
            M1_D =1;
            Delay_2003;
            break;
        }
    }
    else if ( part>=_run_part )
    {
        M1_A =0;
        M1_B =0;
        M1_C =0;
        M1_D =0;
//        part=0;
        f=0;
    }
    return f;
}
//-----------------------------------------------
void TIME0_INIT ( void )
{
//    AUXR &= 0x7F;                //定时器时钟12T模式
    TMOD &= 0xF0;                //清空定时器模式
    TMOD |= 0x01;                //设置定时器模式16位
    TL0 = ( 65536 - FOSC / 12 / 1000 );                // 1ms  的初装值*/
    TH0 = ( 65536 - FOSC / 12 / 1000 ) >> 8;
    ET0 = 1;                                                 //允许定时器0中断
    TF0 = 0;                //清除TF0标志
    EA = 1;                                                 //允许总中断
    TR0 = 1;                //定时器0开始计时
}
//------------------------------
bit M1_flag;
void time0()        interrupt 1                          //定时/计数器 中断
{
//    static bit a=1;
    TF0 = 0;                //清除TF0标志
    TL0 = ( 65536 - FOSC / 12 / 1000 );                //设置定时初值 65536-fosc/12/1000  /*12T时 定时器 每 1ms  的初装值*/
    TH0 = ( 65536 - FOSC / 12 / 1000 ) >> 8;                //设置定时初值        TF1 = 0;                //清除TF1标志
    if ( M1_flag==1 )
    {
        M1_flag=M1_run ( Backward,4000 );
    }
    if ( M1_flag==0 )
    {
        P21=0;   //步数完成
    }
}
void main()
{
    M1_A =0;
    M1_B =0;
    M1_C =0;
    M1_D =0;
    TIME0_INIT (  );//定时器初始化
    while ( 1 )
    {
        Delay100ms();
        P22=~P22;
        if ( P20==0 )//按键启动电机
        {
            M1_flag=1;
            P21=1;//关灯
        }
    };
}
回复

使用道具 举报

ID:1034262 发表于 2022-8-9 09:49 | 显示全部楼层
程序不高效引起。
记住,程序中不要用软件延时,电机驱动脉冲要用中断来处理。我驱动、8个步进电机都丝滑,还有N多数据要处理,多个串口要通讯(115200)。
回复

使用道具 举报

ID:139866 发表于 2022-8-9 10:47 | 显示全部楼层
不如试试freertos?
回复

使用道具 举报

ID:276685 发表于 2022-8-9 12:31 | 显示全部楼层
hxdby 发表于 2022-8-5 16:31
你这个比较高级,我这个控制比较简单,主要就是控制一个流量阀的开合,来控制气体的流量,匀速运动就可以 ...

给你个思路,加个缓冲区.我那个代码n轴联动都不会卡,实际16mhz,avr芯片顺滑无比.你把你的电机怎么发脉冲的放到一个函数里,不要加延时.换到我那个程序里.然后通过控制定时器频率改速度
回复

使用道具 举报

ID:844772 发表于 2022-8-9 12:56 | 显示全部楼层
弄好了吗?我就是觉得不是程序问题,本来应该5v的用3.3v驱动
回复

使用道具 举报

ID:844772 发表于 2022-8-9 13:01 | 显示全部楼层
成功了也说一下原因啊。我总觉得不是程序问题,而且你确定在用3.3驱动5v板子,稍微多几个传感器就不行了,要不你把那几个脚拉高,或者拔掉所有其他负载试试。
回复

使用道具 举报

ID:236035 发表于 2022-8-9 13:07 | 显示全部楼层
1 流量阀用直流电机就行了,就是给电就转,不给电就行。
2 步进电机的时间就是要很精确,我用过PWM输出控制步进电机驱动器,都是在中断内执行控制算法的。如果用延时的方法,确实会不平滑。
回复

使用道具 举报

ID:1041712 发表于 2022-8-9 15:14 | 显示全部楼层
震动是不是因为阻尼没调整?
回复

使用道具 举报

ID:997026 发表于 2022-8-23 22:15 | 显示全部楼层
glinfei 发表于 2022-8-9 13:01
成功了也说一下原因啊。我总觉得不是程序问题,而且你确定在用3.3驱动5v板子,稍微多几个传感器就不行了, ...

这段时间非常忙,一直没有回复,这个问题已经解决了,就是放在中断内就可以了,中断优先级调到最高。注意程序的写法也要发非常注意,程序写的不高效也会出现震动,我用的是wulin提供的程序,运转很好。

确定硬件没有问题,3.3V可以驱动ULV2003,没问题。
回复

使用道具 举报

ID:997026 发表于 2022-8-23 22:19 | 显示全部楼层
这段时间非常忙,一直没空上来回复,这个问题已经解决了,就是把程序放在中断中,1500us执行一次,中断优先级设置为最高。我是放在嘀嗒中断中,或者放在其他定时器中断里都可以。
硬件上没啥问题,3.3V MCU一样可以驱动ULN2003

用的是wulin提供的代码,真的非常感谢wulin的代码,和1kc8210的建议,感谢你们的帮助!
回复

使用道具 举报

ID:997026 发表于 2022-9-4 15:33 | 显示全部楼层
ly1972001 发表于 2022-8-9 13:07
1 流量阀用直流电机就行了,就是给电就转,不给电就行。
2 步进电机的时间就是要很精确,我用过PWM输出控 ...

我这个流量阀非常复杂,主要是需要记住位置,主要的工作量都是在做位置检测,流量阀有8个档位,通过步进电机控制,每个档位对应一个固定的电机位置,每个档位之间可以交叉切换,这样就必须考虑每个档位对应的固定位置不变,无论切到哪个档位,回到当前档位的时候位置必须不变,还有电机的异常处理,比如正在转的过程中断电了,位置如何判断等,非常复杂,需要一套完整的检测算法
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表