yohai26 2022-09-28 23:25 采纳率: 83.3%
浏览 279
已结题

求写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占空比的。

  • 写回答

3条回答 默认 最新

  • weixin_40681229 2022-09-29 15:46
    关注

    #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++;
    

    }

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(2条)

报告相同问题?

问题事件

  • 系统已结题 10月7日
  • 已采纳回答 9月29日
  • 修改了问题 9月29日
  • 创建了问题 9月28日

悬赏问题

  • ¥20 docker里部署springboot项目,访问不到扬声器
  • ¥15 netty整合springboot之后自动重连失效
  • ¥15 悬赏!微信开发者工具报错,求帮改
  • ¥20 wireshark抓不到vlan
  • ¥20 关于#stm32#的问题:需要指导自动酸碱滴定仪的原理图程序代码及仿真
  • ¥20 设计一款异域新娘的视频相亲软件需要哪些技术支持
  • ¥15 stata安慰剂检验作图但是真实值不出现在图上
  • ¥15 c程序不知道为什么得不到结果
  • ¥40 复杂的限制性的商函数处理
  • ¥15 程序不包含适用于入口点的静态Main方法