求写pwm控制步进电机运行代码

单片机型号stc15w201s
42步进电机,tb6600驱动器,步距角1.8,细分3200,单片机与pwm方波为共同电路的。
脚位介绍:
①、p1.1脚和p1.2脚为读取解析pwm方波的。
②、p1.4脚给步进电机驱动器脉冲。
③、p3.7脚给步进电机驱动器方向。
④、p1.0脚led等,当有动作运行led闪烁,平时常亮。
总结:p1.1或1.2脚读取到pwm,p1.4和3.7脚给驱动器脉冲方向(正转或反转),p1.0的led灯闪烁。另外步进电机的旋转快慢是依照pwm占空比的。

#include <STC15.H>
/**********************************************************************************
*通过阅读tb6600驱动器资料:tb6600只需要脉冲输出与控制方向。
*tb6600驱动器的电流是由驱动器上的拔动开关控制的,不需要PWM控制电流。

*不知道你为什么要用两个IO口来检测PWM的状态
*你是想一个开上升沿中断检测?
*一个开下降沿中断检测?
*本程序只用了一个IO口检测。实时检测上下沿变化,再取计时器中的时间判断
***********************************************************************************/
//定义端口
#define pwmOut P14 //PWM的输出
#define pwmDir P37 //PWM的方向

//一个IO来解析PWM,然后用LED体现出来
#define scanpwm P11 //解析PWM
//#define scanpwmL P12 //解析PWM

#define key P12 //一个按键,用来触发步进电机动作 默认高电平,按下低电平

//因为使用了步进电机驱动器,控制器通过拔码开关的方式调整电流,
//PWM的占空比是用来调整电流的,在这里对驱动步进电机没有意义了而,
//PWM的占空比设置个50%就可以了 pwmPeriod /2;
//定时器是0.1MS的中断,pwmPeriod = 20就是2MS一个周期,500HZ,满足低于1KHZ的要求。
//通过更改pwmPeriod,可以实现脉冲频率的变化,会改变步进电机的速度
//pwmPeriodH < pwmPeriod
#define pwmPeriod 20 //PWM的总时长 = 20*0.1=2ms 可以根据实际需求改小改大
#define pwmPeriodH 10 //PWM的高电平时长 = 10 *0.1=1ms

//LED
#define ledtDisplayTime 3000 //LED显示时间
#define ledOn P10 = 0 //定义这个是为了方便修改LED亮/灭对应的电平
#define ledOff P10 = 1

//***********************************************************************************************************定义变量
//1,步进电机
unsigned int turns_TO_Impulse; //步进电机的圈数转脉冲数
unsigned int turns_TO_ImpulseH; //步进电机的圈数转脉冲数 整数部分
unsigned int turns_TO_ImpulseL; //步进电机的圈数转脉冲数 整数部分
unsigned int scanTurns_TO_Impulse; //步进电机的圈数转脉冲数的自计算

bit pwmOutEnable = 0; //使能PWM输出

unsigned char pwmTime; //PWM的电平翻转时间计时
unsigned char pwmOutStep; //PWM输出步骤

//2,检测步进电机
unsigned int scanpwmtime; //检测步进电机的计时
unsigned int scanpwmtimeH; //PWM的高电平计时 = *0.1ms
unsigned int scanpwmtimeL; //PWM的低电平计时 两者相差等于或约等于PWM的总时间
unsigned char scanPwmStep; //检测PWM的步骤

//3,LED
unsigned int ledtime; //LED闪烁计时
bit ledTwinkleEnable = 0; //LED闪使能
unsigned char ledStep; //LED输出步骤
//**************************************************************************************************************结束定义变量

//************************************************************************************************************程序
//一些IO口的初始化
void GpioInit(void)
{
ledOff; //LED端口的输出接个电阻,LED,接上拉电源
pwmOut = 0; //默认输出低电平---检测端口初始也应该是低电平
pwmDir = 0; //视情况而定
}

//开一个定时器中断
void Timer0Init(void) //100微秒@12.000MHz
{
AUXR |= 0x80; //定时器时钟1T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0x50; //设置定时初始值
TH0 = 0xFB; //设置定时初始值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时

IE = 0X82;     //开启定时器中断

}

//PWM输出程序
//输出脉冲 turns_TO_Impulse
void PwmOut(void)
{
if(pwmOutEnable == 0) //如果没有使能PWM脉冲输出
return;
switch(pwmOutStep)
{
case 0: //PWM输出高电平
pwmTime = 0; //清计时
pwmOut = 1; //PWM输出高电平
pwmOutStep = 1; //跳到下一步执行
break;
case 1: //PWM输出低电平
if(pwmTime >= (pwmPeriodH))
{
pwmTime = 0;
pwmOut = 0;
pwmOutStep = 2;
}
break;
case 2: //PWM输出低电平结束
if(pwmTime >= (pwmPeriod-pwmPeriodH))
{
pwmTime = 0; //清计时
pwmOut = 1; //PWM输出高电平
pwmOutStep = 1; //跳到下一步执行--------------视圈数到否?是否关闭了使能?
scanTurns_TO_Impulse++; //脉冲计数
if(scanTurns_TO_Impulse >= turns_TO_Impulse) //达到脉冲计数值后,关闭PWM输出
{
pwmOutEnable = 0;
}
}
break;
default:
pwmOutEnable = 0;
break;
}
}

//步进电机驱动-----这个程序在你需要步进电机转动时,再调用
//dir 方向:取值为1或0
//turnsNumbers 转数:有2种表达方式
// 1,角度值 例如:720度, 900度,等等 1圈 = 360度
// 2,圈数 例如:2圈, 2.5圈,等等
//mode 0 = 角度值算法 1 = 圈数算法
void motor_Drive(bit dir,double turnsNumbers,bit mode)
{
unsigned int turns_TO_Impulse; //步进电机的圈数转脉冲数
unsigned int turns_TO_ImpulseH; //步进电机的圈数转脉冲数 整数部分
unsigned int turns_TO_ImpulseL; //步进电机的圈数转脉冲数 整数部分
//步进电机的方向
pwmDir = dir; //根据实际情况代入1或0

//计算步进电机的圈数
//0.1125 = 1.8/16 = 360/3200 一个脉冲的度数
if(mode == 0)//度数
{ 
    turns_TO_Impulse = (unsigned int)(turnsNumbers/0.1125);
}
else//圈数
{
    turns_TO_ImpulseH = (unsigned int) turnsNumbers;        //取出整数部分
    turns_TO_ImpulseH *= 3200;                                            //得到整数部分的脉冲数
    turns_TO_ImpulseL = (unsigned int) (turnsNumbers*10000);//将小数*10000,移到整数
    turns_TO_ImpulseL %= 10000;//取出小数
    turns_TO_ImpulseL = turns_TO_ImpulseL/1152;                    //得到小数部分的脉冲数       0.1125 = 1.8/16 = 360/3200
    turns_TO_Impulse = turns_TO_ImpulseH + turns_TO_ImpulseL;
}

//使能步进电机的输出
pwmOutStep = 0;
pwmOutEnable = 1;

}

//检测PWM是否工作--------------------------------------------------------------------------------------------------------------------------
//PWM的初始状态是0 当检测到1时,视为PWM开始
//PWM结束输出时,PWM的输出电平是低电平
//scanpwmtimeH PWM的高电平计时 通过这两个变量,可以得到PWM的占空比(程序开头已说明,无需要PWM驱动)
//scanpwmtimeL PWM的低电平计时
void scanPWM(void)
{
switch(scanPwmStep)
{
case 0://检测是否有高电平产生
if(scanpwm == 1)
{
scanpwmtime = 0;
scanPwmStep = 1;
}
break;
case 1://检测是否有低电平产生
if(scanpwm == 0)
{
scanpwmtimeH = scanpwmtime; //获取高电平的时间 高电平时间与低电平时间,要相互印证。比如说一直高,一直低
scanpwmtime = 0;
scanPwmStep = 2;
}

        if(scanpwmtime > pwmPeriod*2) //当高电平持续过长/异常(未接到PWM)
        {
            scanPwmStep = 0;                //如果电平一直为高,会造成0-1之间的循环,不影响LED灯的状态
            ledTwinkleEnable = 0;        //关闭LED闪功能
            scanpwmtimeH = 0;                //异常时清除PWM高低电平的计时
            scanpwmtimeL = 0;
        }
    break;        
    case 2://检测是否有高电平产生
        if(scanpwm == 1)
        {            
            scanpwmtimeL = scanpwmtime;    //获取高电平的时间
            scanpwmtime = 0;
            scanPwmStep = 1;
            //       高低电平的时间总长 > PWM总周期的一半            高低电平的时间总长 < PWM总周期的1.5if(((scanpwmtimeL+scanpwmtimeH)> (pwmPeriod/2)) &&((scanpwmtimeL+scanpwmtimeH) < (pwmPeriod/2 + pwmPeriod)))//判断一下高低电平时间是否符合PWM的设定
            {
                ledStep = 0;
                ledTwinkleEnable = 1;    //使能LED闪功能
            }
        }
        
        if(scanpwmtime > pwmPeriod*2) //当低电平持续过长说明PWM结束
        {
            scanPwmStep = 0;
            ledTwinkleEnable = 0;        //关闭LED闪功能
            scanpwmtimeH = 0;                //异常时清除PWM高低电平的计时
            scanpwmtimeL = 0;
        }
    break;        
}

}

//LED输出------------------------------------------------------------------------------------------------------------------------------------
void ledOut(void)
{
if(ledTwinkleEnable == 0) //这个使能由检测来决定
{
ledOn;
return;
}

switch(ledStep)
{
    case 0://LED灭
    ledOff;
    ledtime = 0; //清计时 = 0
    ledStep = 1; //跳到下一步执行
    break;    
    case 1://LEDif(ledtime >= ledtDisplayTime)
        {
            ledOn;
            ledtime = 0;
            ledStep = 2; //跳到下一步执行
        }
    break;
    case 2://LEDif(ledtime >= ledtDisplayTime)
        {
            ledOff;
            ledtime = 0; //清计时 = 0
            ledStep = 1; //跳到下一步执行        视是否关闭了使能?
        }
    break;
}    

}

//一个简单的按键程序,用来调用步进电机的动作
//根据实际情况,按键的脚位调整位置
//按下去后,按键上的电平为0
void scanKey(void)
{

if(key == 1)
    return;

if(key == 0)
{
    unsigned int a,b;
    //一段延时后动作,如果觉得按键后很久才动作,改小1000的值
    //未做消抖处理,按下后速度松开
    for(a = 0; a < 1000;a++)
     for(b = 0; b <100;b++);
    //                            方向       转数          转数的模式
    //motor_Drive(bit dir,double turnsNumbers,bit mode);
    //motor_Drive(0,2.5,1);                //向一个方向转900度
    motor_Drive(1,900,0);                    //向反方向转900度
}    

}

//主程序------------------------------------------------------------------------------------------------------------------------------------------
//scanKey();//检测按键 一定要加一个按键,否则电机不会动作
void main(void)
{
//初始华
GpioInit();
Timer0Init();

//主程序
while(1)
{
    scanKey();//检测按键                    一定要加一个按键,否则电机不会动作
    PwmOut();//脉冲输出
    scanPWM();//检测是否有PWM输出
    ledOut();//灯输出
}

}

//定时器中断-------------------------------------------------------------------------------------------------------------------------------------------
void timer0_int (void) interrupt 1
{
//PWM电平计时变量
pwmTime++;

//检测PWM的输出电平的计时变量
scanpwmtime++;

//LED灯亮/灭的间隔时间
ledtime++;

}

最好把原理图和驱动器的资料贴一下,步进电机控制细节还是挺多的,参数不合适会造成失步等问题,做过类似型号的,如果兼容就省事了

int PUL = 7; //定义脉冲引脚
int DIR = 6; //定义方向销
int ENA = 5; //定义启用引脚
int RESET = 4; //定义复位传感器
/**

  • TB6600驱动器 . 42两相四线步进电机
  • /
    void setup() {
    pinMode (PUL, OUTPUT);
    pinMode (DIR, OUTPUT);
    pinMode (ENA, OUTPUT);
    pinMode (RESET,INPUT);
    Serial.begin(9600);
    // 初始化复位 - 转3圈
    for (int i = 0; i < 4800; i++) //前进4800步 SW1=OFF,SW2=ON,SW3=OFF(每圈1600脉冲)
    {
    digitalWrite(DIR, LOW); // 定义正转
    digitalWrite(ENA, HIGH);// 启动
    digitalWrite(PUL, HIGH);// 输出脉冲
    delayMicroseconds(2000);
    digitalWrite(PUL, LOW);
    // 复位传感器
    int reset = digitalRead(RESET);
    if(c == 0) {
    // 监测到达复位位置后停止转动
    break;
    
    }
    delayMicroseconds(2000);
    }
    }
    void loop() {
    for (int i = 0; i < 1600; i++) //正转1圈
    {
    digitalWrite(DIR, LOW);
    digitalWrite(ENA, HIGH);
    digitalWrite(PUL, HIGH);
    delayMicroseconds(50);
    digitalWrite(PUL, LOW);
    delayMicroseconds(50);
    }
    delay(10000); // 暂停10秒
    for (int i = 0; i < 1600; i++) //倒转1圈
    {
    digitalWrite(DIR, HIGH);
    digitalWrite(ENA, HIGH);
    digitalWrite(PUL, HIGH);
    delayMicroseconds(50);
    digitalWrite(PUL, LOW);
    delayMicroseconds(50);
    }
    }