用 PWM 细分步进电机,包含原理和程序
农民讲习所
这是俺一个关于打印机项目中步进马达中的内容,使用的原理为电流矢量图(a),为减少矢量表的误差,改进为(g).(误
差大的时候,Ia和Ib的绝对值变大,角度不变,即比例不变)
磁场合成决定步进马达当前的位置。
我们常用的是图(b),用电压驱动时和线圈个数有关系,直接驱动它们,合成的最小角度都是固定的。
细分最常见的是电流细分,电流引起的磁场的合成就决定马达的角度。所以我们一般用电流合成图来代替磁场合成图。
用电流合成电路复杂一点(用MCU+DA+L298实现也不难)。
这里俺用PWM方式,用控制电压的方式代替电流。原因:在电压稳定后,线圈的电流和电压成正比。稳定是PWM细分的
要点,也决定马达速度不能太高。
在细分的情况下,电机分两相和四相是没意义的。可以把四相电机当两相使用。
下面是俺的程序,使用MEGA16,因为有PWM。
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//2相步进马达驱动
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
#define Step_OutMode() DDRB|=BIT(6)|BIT(7)
#define Step_InMode() DDRB&=~(BIT(6)|BIT(7))
#define Step_ReadIO() (PORTB&( ~(BIT(6)|BIT(7)) ))
#define Step_WriteIO(x) PORTB=x
struct Motor2P_Table{ //细分表结构
unsigned char mQuadrant; //象限0-3
unsigned char mX_Pwm; //x输出的PWM
unsigned char mY_Pwm; //y输出的PWM
};
struct Motor2P{
unsigned char mTableId; //细分表ID
unsigned char mTask; //马达驱动的任务转移号
unsigned char mSaveTimer; //马达驱动节电模式使用的计数器
unsigned char mReg; //马达驱动使用的寄存器
unsigned char mTimer; //对2MS的计数器,时间扩展
unsigned int mSteps; //马达要走的步数
unsigned char mDirect; //马达走动方向
unsigned char mStepRunTimer; //步进时输出有效的时间寄存器
unsigned char mStepIdleTimer; //步进时输出节电的时间寄存器
unsigned char mStepCount; //细分表走动步长。1-2-4-8-16
};
//------------------------------------------------------------
#ifdef dMotor2P_Main
#define Motor2P_Head
#else
#define Motor2P_Head extern
#endif
Motor2P_Head struct Motor2P sMotor2P; //定义变量
Motor2P_Head void Motor_Steps(unsigned char fDirect,unsigned int mSteps);//马达走动.fDirect=1,0->3,顺时
针.步数.要先检查Motor_State()==1才调用
Motor2P_Head void Motor_Init(void); //马达构造函数
Motor2P_Head void Motor_Destory(void); //马达析构函数
//返回马达状态。>0:空闲
#define Motor_State() (sMotor2P.mSteps==0)
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//2相步进马达驱动
//采用PWM细分。
//原理:正弦波矢量扩展到正方形矢量
//细分数为2的整数
//节电时电流为平常的N/8。
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
#include
#include
#include
#include "Const.h"
#include "System.h"
#define dMotor2P_Main
#include "Motor2P.h" //主程序中定义,直接定义变量
#define dStepRunTime 1 //N*2ms,输出有效的时间
#define dStepIdleTime 1 //输出节电的时间.1->无.改变速度在此改变。
#define dSaveCurrentLimit 10 // dSaveCurrentLimit/16处为节电的比例点。
#define dIdleTimeMax 100 //进入IDLE需要的空闲时间.基于2ms.
enum Motor_State{
eMotor_Idle_Process, //下电过程
eMotor_Stop_State, //未启动
eMotor_PowerOn_Process, //上电
eMotor_PowerOnDelay_Process, //上电到正常工作的延时
eMotor_Normal_State, //正常工作中
eMotor_StepDelay_Process, //进入步进处理延时过程
eMotor_IdleDelay_Process, //进入节电处理延时过程
};
#define Step_Quadrant0 BIT(6)|BIT(7)
#define Step_Quadrant1 BIT(7)
#define Step_Quadrant2 0
#define Step_Quadrant3 BIT(6)
//0-90度的细分的PWM数据(0-255).n是细分=16.x=cos(a)*255,y=a/tg(a)(正弦转多边,减少误差).逆时针表,半周期。
const struct Motor2P_Table cMotor2P_Table[]=
{
//第一象限 +x,+y
Step_Quadrant0,255,0 ,
Step_Quadrant0,254,25 ,
Step_Quadrant0,251,50 ,
Step_Quadrant0,244,74 ,
Step_Quadrant0,237,98 ,
Step_Quadrant0,226,121 ,
Step_Quadrant0,211,141 ,
Step_Quadrant0,195,160 ,
Step_Quadrant0,180,180 ,
Step_Quadrant0,160,195 ,
Step_Quadrant0,141,211 ,
Step_Quadrant0,121,226 ,
Step_Quadrant0,98 ,237 ,
Step_Quadrant0,74 ,244 ,
Step_Quadrant0,50 ,251 ,
Step_Quadrant0,25 ,254 ,
//第二象限 -x,+y
Step_Quadrant1,0 ,255 ,
Step_Quadrant1,25 ,254 ,
Step_Quadrant1,50 ,251 ,
Step_Quadrant1,74 ,244 ,
Step_Quadrant1,98 ,237 ,
Step_Quadrant1,121,226 ,
Step_Quadrant1,141,211 ,
Step_Quadrant1,160,195 ,
Step_Quadrant1,180,180 ,
Step_Quadrant1,195,160 ,
Step_Quadrant1,211,141 ,
Step_Quadrant1,226,121 ,
Step_Quadrant1,237,98 ,
Step_Quadrant1,244,74 ,
Step_Quadrant1,251,50 ,
Step_Quadrant1,254,25 ,
//第三象限 -x,-y
Step_Quadrant2,255,0 ,
Step_Quadrant2,254,25 ,
Step_Quadrant2,251,50 ,
Step_Quadrant2,244,74 ,
Step_Quadrant2,237,98 ,
Step_Quadrant2,226,121 ,
Step_Quadrant2,211,141 ,
Step_Quadrant2,195,160 ,
Step_Quadrant2,180,180 ,
Step_Quadrant2,160,195 ,
Step_Quadrant2,141,211 ,
Step_Quadrant2,121,226 ,
Step_Quadrant2,98 ,237 ,
Step_Quadrant2,74 ,244 ,
Step_Quadrant2,50 ,251 ,
Step_Quadrant2,25 ,254 ,
//第四象限 +x,-y
Step_Quadrant3,0 ,255 ,
Step_Quadrant3,25 ,254 ,
Step_Quadrant3,50 ,251 ,
Step_Quadrant3,74 ,244 ,
Step_Quadrant3,98 ,237 ,
Step_Quadrant3,121,226 ,
Step_Quadrant3,141,211 ,
Step_Quadrant3,160,195 ,
Step_Quadrant3,180,180 ,
Step_Quadrant3,195,160 ,
Step_Quadrant3,211,141 ,
Step_Quadrant3,226,121 ,
Step_Quadrant3,237,98 ,
Step_Quadrant3,244,74 ,
Step_Quadrant3,251,50 ,
Step_Quadrant3,254,25 ,
};
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//马达走动
//入口:方向.fDirect=1,0->3,顺时针.
// 步数
//要先检查Motor_State()==1才调用
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
void Motor_Steps(unsigned char fDirect,unsigned int mSteps)
{
sMotor2P.mDirect=fDirect;
sMotor2P.mSteps =mSteps;
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//输出马达能量(电压->电流)
//入口:mId=细分驱动表
// mNum=节电比例,16倍缩小,mNum倍放大
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
static void InMotor_Energy(unsigned char mId,unsigned char mNum)
{
const struct Motor2P_Table *pcMotor2P_Table;
unsigned char i;
unsigned char x;
unsigned char y;
pcMotor2P_Table=&cMotor2P_Table[mId];
x=(unsigned int)( pcMotor2P_Table->mX_Pwm *mNum) >>4;
y=(unsigned int)( pcMotor2P_Table->mY_Pwm *mNum) >>4;
PushInterrupt();
CLI();
i=Step_ReadIO()|pcMotor2P_Table->mQuadrant; //方向,因为PWM变化慢,所以方向后改变
OCR1A=x;
OCR1B=y;
Step_WriteIO(i);
PopInterrupt();
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//马达走动一步
//入口:方向.fDirect=1,0->3,顺时针
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
static void InMotor_DirecctStep(unsigned char fDirect)
{
if(fDirect){
if(sMotor2P.mTableId==0)sMotor2P.mTableId=sizeof(cMotor2P_Table)/sizeof(struct
Motor2P_Table)-sMotor2P.mStepCount;
else sMotor2P.mTableId-=sMotor2P.mStepCount;
}
else {
sMotor2P.mTableId+=sMotor2P.mStepCount;
if(sMotor2P.mTableId==sizeof(cMotor2P_Table)/sizeof(struct Motor2P_Table) )sMotor2P.mTableId=0;
}
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//马达构造函数
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
void Motor_Init(void)
{
//变量初始化
memset( (unsigned char *)(&sMotor2P),0,sizeof(struct Motor2P) );
sMotor2P.mTimer=sMotor2P.mStepRunTimer=dStepRunTime; //输出有效的时间,设置步进时间
sMotor2P.mStepIdleTimer=dStepIdleTime; //输出节电的时间
sMotor2P.mSaveTimer = dIdleTimeMax; //可以判断忙否。>0不忙.
sMotor2P.mStepCount=1; //最大16无插值.1-2-4-8-16.
//T2初始化。马达驱动
// OCR2=106; //马达定时时间,决定马达的最大速度
OCR2=150; //马达定时时间,决定马达的最大速度
TCCR2=BIT(WGM21)|BIT(CS21);
TIMSK|=BIT(OCIE2);
//PWM初始化,使用T1的PWM,8bit
OCR1A=OCR1B=0;
TCCR1B=BIT(CS10); //1分频
TCCR1A=BIT(COM1A1) | BIT(COM1B1) |BIT(WGM10); //8位PWM,OC1A,OC1B
DDRB|=BIT(1)|BIT(2);
//控制口初始化
Step_OutMode();
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//马达析构函数
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
void Motor_Destory(void)
{
TCCR2=0;
TIMSK&=~BIT(OCIE2);
TCCR1B=TCCR1A=0;
DDRB&=~(BIT(1)|BIT(2));
Step_InMode();
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//中级线程
//马达驱动使用
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
#pragma interrupt_handler timer2_comp_isr:iv_TIMER2_COMP
void timer2_comp_isr(void)
{
switch(sMotor2P.mTask){
case eMotor_Idle_Process: //下电过程.==0
if(--sMotor2P.mReg){ //sMotor2P.mReg此时为衰减比例
if( --sMotor2P.mTimer ==0){ //延时
sMotor2P.mTimer= sMotor2P.mStepRunTimer + sMotor2P.mStepIdleTimer ;
InMotor_Energy( sMotor2P.mTableId, sMotor2P.mReg );
}
break;
}
else sMotor2P.mTask = eMotor_Stop_State; //进入STOP状态
case eMotor_Stop_State: //马达未启动
//判断起始条件sMotor2P.mSteps>0;
if( sMotor2P.mSteps == 0)break;
sMotor2P.mTask = eMotor_PowerOn_Process;
case eMotor_PowerOn_Process: //上电启动
if( sMotor2P.mReg < dSaveCurrentLimit ){
if( --sMotor2P.mTimer ==0){ //延时
sMotor2P.mTimer= sMotor2P.mStepRunTimer + sMotor2P.mStepIdleTimer ;
InMotor_Energy( sMotor2P.mTableId, ++sMotor2P.mReg ); //衰减比例递增加大电流
}
break;
}
sMotor2P.mTask = eMotor_PowerOnDelay_Process; //进入正常工作模式前的延时
break;
case eMotor_PowerOnDelay_Process: //进入步进处理延时过程
if( --sMotor2P.mTimer ==0){
sMotor2P.mTimer= sMotor2P.mStepRunTimer; //开始延时
sMotor2P.mTask = eMotor_Normal_State; //进入正常工作模式
}
else break;
lMotor_Normal_State:
case eMotor_Normal_State: //正常工作中
if( sMotor2P.mSteps == 0){
//节电模式计时器增加
if( ++sMotor2P.mSaveTimer == dIdleTimeMax ){
sMotor2P.mReg = dSaveCurrentLimit; //衰减比例
sMotor2P.mTask = eMotor_Idle_Process; //下电过程
}
}
else {
sMotor2P.mSaveTimer=0;
//开始步进
InMotor_DirecctStep( sMotor2P.mDirect ); //走动细分表
InMotor_Energy( sMotor2P.mTableId, 16 ); //输出电流
sMotor2P.mTask = eMotor_StepDelay_Process; //进入步进处理过程
}
break;
case eMotor_StepDelay_Process: //进入步进处理延时过程
if( --sMotor2P.mTimer ==0){
//节电处理
InMotor_Energy( sMotor2P.mTableId, dSaveCurrentLimit );
sMotor2P.mTask = eMotor_IdleDelay_Process; //进入节电处理延时过程
sMotor2P.mTimer= sMotor2P.mStepIdleTimer; //开始延时
}
else break;
case eMotor_IdleDelay_Process: //进入节电处理延时过程
if( --sMotor2P.mTimer ==0){
sMotor2P.mTimer= sMotor2P.mStepRunTimer; //延时时间
sMotor2P.mTask=eMotor_Normal_State; //转入正常工作状态,等待下一步。
sMotor2P.mSteps -- ;
if(sMotor2P.mSteps == 0)Message_TailPushOnlyMess( nMotor2P_Stop); //发送马达走完消
息。
goto lMotor_Normal_State;
}
break;
}
}
驱动分驱动电压和维持电压,可以大幅度降低电流。长时间不用,STOP。(能STOP的原因,是只要再上电按STOP前的
矢量图输出就可以保证位置不动。)
从矢量合成表很容易就推出我们常用的四相的驱动图。
如果把矢量合成表的个数只保留4个,就是最普通的两相驱动方式:
Ia=Max,Ib=0;->Ia=0,Ib=Max;->Ia=-Max,Ib=0;->Ia=0,Ib=-Max;->
PWM方式只适合不太高的速率场合
在这个条件下不存在抖动。所有的细分模式都会引起电流的增大,反过来说,电流的增大提高了扭矩力。
引入节电模式,多长时间电机都不会发烫。
四相和两相比较,本质就是想提高步进精度,仅此而已。从矢量图的角度来说,他们没有区别。这也是细分后,四相没
有意义的道理。
很多场合都可以看到‘细分后,四相没有意义’这句话,俺把它从原理阐述出来而已。
效果比较:
细分前,电流为1A,细分后为1.2A左右,加入节电模式,电流只有120mA左右,效果惊人。
力矩变大是细分的必然效果。
步进角度变小。
运行非常稳定,没有失步,没有振动。(细分就是解决此类问题才提出来的,步进角度变小只是它的副作用)
按俺的程序,不需要缓启动和缓停止概念。这里是用缓电流的方式解决的。
本文档为【用PWM_细分步进电机_包含原理和程序】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑,
图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。