找回密码
 立即注册
楼主: 梁***

51开源 STC步进电机驱动 程序, 直接驱动, 或驱动步进电机驱动器 一共8个程序

[复制链接]
  • TA的每日心情
    开心
    4 天前
  • 签到天数: 1 天

    [LV.1]初来乍到

    0

    主题

    2

    回帖

    12

    积分

    新手上路

    积分
    12
    发表于 前天 10:22 | 显示全部楼层
    请教梁工及各位前辈大侠,我把这个移植到STC12C5A60S2,PCA0 输出10ms周期的波形,改好几天都没改明白,麻烦各位看下。

    #include "STC12C5A60S2.h"
    #include <intrins.h>

    #define MAIN_Fosc                24000000UL        //定义主时钟

    #define u8  unsigned char
    #define u16 unsigned int
    #define u32 unsigned long

    sbit STRAT_STOP = P2^0;       //开始键



    void Delay_ms(unsigned char ms)
    { // 延时子程序 毫秒
            unsigned char i;
            while(ms--)
            {
                    for(i = 0; i< 82; i++)
                    {
                            _nop_();_nop_();_nop_();_nop_();
                    }
            }
    }

    #define  PCA1_16bit_Timer()  CCAPM0 = (0x48 + 1)        /* 16位软件定时器模式 */
    #define                PCA_VECTOR                        7
    #define                TIMER0_VECTOR                1


    /*************        功能说明        **************

    用PCA高速脉冲输出控制步进电机驱动器.
    为了简单, 利于初学者, 本例使用线性加减速, 如要使用别的加减速算法, 用户自行设计.

    使用外设:
    Timer0: 工作于1ms中断, 提供1ms时隙标志和串口超时处理.
    Timer2: 串口1波特率.
    串口1:  命令控制, 串口设置115200,8,1,n.
    PCA0:   从P2.3输出驱动脉冲, 低驱动, 接步进电机驱动器脉冲输入端(一般是光耦输入, 低有效).
            从P2.0输出转向信号, 接步进电机驱动器方向输入端(一般是光耦输入, 低有效), 1:顺时针(正转), 0:逆时针(反转).

    串口命令设置:
    L1,500,1000   --> 马达1以500Hz正转1000个脉冲, 脉冲数为0则连续转动.
    R1,500,1000   --> 马达1以500Hz反转1000个脉冲, 脉冲数为0则连续转动
    s             --> 停止所有电机

    ******************************************/


    /*************        本地变量声明        **************/

    u16        CCAP1_tmp;

    //================== 步进电机相关变量定义 ===================
    sbit        P_M1_DIR   = P1^0;        // 运行方向, 接步进电机驱动器方向输入端(一般是光耦输入, 低有效), 1:顺时针(正转), 0:逆时针(反转)
    sbit        P_M1_PULSE = P1^3;        // 驱动脉冲, 低驱动, 接步进电机驱动器脉冲输入端(一般是光耦输入, 低有效).

    bit                B_M1_RunEn;                //运行允许
    bit                B_f1_update;        //请求刷新频率值
    bit                B_TIMER1_12T;        //定时器1时钟模式
    u16                f1_period;                //当前频率对应的周期(半周期)(中断使用, 应用层不可操作)
    u16                f1_period_set;        //需要刷新的目标频率对应的周期(半周期)
    u16                f1;                                //当前频率
    u16                f1_set;                        //目标频率
    u16                f1_step;                //加减速频率变化的步长
    u16                M1_UpPulse;                //加(减)速脉冲数
    u16                M1_PulseCnt;        //电机运行总脉冲数, 为0则连续运行
    u16                M1_DownCnt;                //运行到要减速输出的脉冲数
    //===========================================================

    bit        B_1ms;        //1ms时隙标志

    /*************        本地函数声明        **************/
    void        PCA_config(u8 clk);                //io: 选择IO, 0: 选择P12 P17 P16 P15 P14,   1:        选择P22 P23 P24 P25 P26,   2: 选择P74 P70 P71 P72 P73,   3: 选择P35 P33 P32 P31 P30.
                                                                                    //clk: 选择时钟, 0: 12T,  1: 2T, 2: Timer0溢出率, 3: ECI引脚输入, 4: 1T,  5: 4T,  6: 6T,  7: 8T
    u8                Timer0_Config(u8 t, u32 reload);        //t=0: reload值是主时钟周期数,  t=1: reload值是时间(单位us), 返回0正确, 返回1装载值过大错误.
    u16                GetStep(u16 f, u16 f_set);        // 计算速度变化步进长度
    void        GetFreq1(void);                                // 计算加减速频率
    void        StopMotor1(void);                        // 停止运行一个电机
    void        RunMotor1(u16 p);                        // 启动运行一个电机



    /******************** 主函数 **************************/
    void main(void)
    {
            P_M1_DIR   = 1;        // 运行方向, 接步进电机驱动器方向输入端(一般是光耦输入, 低有效), 1:顺时针(正转), 0:逆时针(反转)
            P_M1_PULSE = 1;        // 驱动脉冲, 低驱动, 接步进电机驱动器脉冲输入端(一般是光耦输入, 低有效).

            PCA_config(1);        //io: 选择IO, 0: 选择P12 P17 P16 P15 P14,   1:        选择P22 P23 P24 P25 P26,   2: 选择P74 P70 P71 P72 P73,   3: 选择P35 P33 P32 P31 P30.
                                                    //clk: 选择时钟, 0: 12T,  1: 2T, 2: Timer0溢出率, 3: ECI引脚输入, 4: 1T,  5: 4T,  6: 6T,  7: 8T
            Timer0_Config(0, MAIN_Fosc / 1000);        //t=0: reload值是主时钟周期数,  t=1: reload值是时间(单位us)

            EA = 1;
           
            B_M1_RunEn = 0;
            CCAPM0 &= ~0x04;        //禁止高速输出脉冲

            while (1)
            {
                    if(B_1ms)        //1ms时隙
                    {
                            B_1ms = 0;

                            if(B_M1_RunEn)        //加减速处理
                            {
                                    GetFreq1();
                                    if(f1 < 100)
                                    {
                                            B_M1_RunEn = 0;                //停止
                                            P_M1_DIR   = 1;        // 运行方向
                                            CCAPM0 &= ~0x04;        //禁止高速输出脉冲
                                    }
                            }

                            if(STRAT_STOP==0)
                            {
                                    Delay_ms(10);//延时去抖动
                                    if(STRAT_STOP==0)
                                    {
                                            while(STRAT_STOP==0);//等待按键释放
                                            if(!B_M1_RunEn) f1 = 200;        //电机未启动则从200HZ开始启动
                                           
                                            f1_set=1000;
                                            RunMotor1(10000);
                                    }
                            }
                    }
            }
    }
    /**********************************************/

    #define        UpTime        100                //加(减)速时间(ms)

    u16        GetStep(u16 f, u16 f_set)        //计算速度变化步进长度
    {
            u16        i;
            M1_UpPulse = (u16)((u32)(f + f_set)*UpTime / 2000);        // 理论加速脉冲数
            if(f_set >= f)        f_set = f_set - f;                //计算频率差
            else                        f_set = f - f_set;                //计算频率差
            i = f_set / UpTime;                                // 加(减)速步进
            if(i == 0)        i = 1;        //步进不能为0
            return        i;                        //返回加减速步进值
    }

    void        StopMotor1(void)                //停止运行一个电机
    {
            f1_set  = 95;        //小于100Hz则停止
            f1_step = GetStep(f1, f1_set);
    }

    //========== 准备好 "当前频率f1 目标频率f1_set 运行总脉冲数" 后才能启动运行 =================
    void        RunMotor1(u16 p)        //启动运行一个电机, p为要运行的脉冲数
    {
            u16        pulse;
            f1_step = GetStep(f1, f1_set);        //计算步进
            if(p != 0)        //运行总脉冲数非0才有开始减速脉冲数
            {
                    pulse = M1_UpPulse * 2;                //加减速脉冲数之和 = M1_UpPulse * 2
                    if(p >= pulse)        pulse = M1_UpPulse;                        //运行脉冲数 >= 加减速脉冲数之和, 则减速脉冲数按理论计算值
                    else                        pulse = p / 2;        //脉冲数 <  加减速脉冲数之和, 则平分脉冲
                    pulse = p - pulse;                                                // 电机开始减速需要走过的脉冲数;
            }
            else        pulse = 0;

            EA = 0;        //临界保护
            M1_PulseCnt = p;
            M1_DownCnt  = pulse;
            B_M1_RunEn  = 1;
            CCAPM0 |= 0x04;        //允许高速输出脉冲
            EA = 1;
    }

    /************************************/
    void                GetFreq1(void)        // 计算加减速频率
    {
            F0 = 0;
            if(f1 < f1_set)                //当前速度小于目标速度, 加速
            {
                    F0 = 1;                        //需要调速
                    f1 += f1_step;
                    if(f1 > f1_set)        f1 = f1_set;                //目标频率已到
            }
            else if(f1 > f1_set)                //当前速度大于目标速度, 减速
            {
                    F0 = 1;                        //需要调速
                    if(f1 < f1_step)        f1 = 0;
                    else                                f1 -= f1_step;
                    if(f1 < f1_set)                f1 = f1_set;        //目标频率已到
            }
            if(F0)        //需要调速
            {
                    f1_period_set = MAIN_Fosc/2/2/f1;        //PCA时钟2T, 半周期
                    B_f1_update = 1;        //请求刷新
            }
    }


    //========================================================================
    // 函数: void        PCA_config(u8 io, u8 clk)
    // 描述: PCA配置函数。
    // 参数: io: 选择IO, 0: 选择P12 P17 P16 P15 P14,   1:        选择P22 P23 P24 P25 P26,   2: 选择P74 P70 P71 P72 P73,   3: 选择P35 P33 P32 P31 P30.
    //       clk: 选择时钟, 0: 12T,  1: 2T, 2: Timer0溢出率, 3: ECI引脚输入, 4: 1T,  5: 4T,  6: 6T,  7: 8T
    // 返回: none.
    // 版本: VER1.0
    // 版本: V1.0, 2016-5-10
    // 备注:
    //========================================================================
    void        PCA_config(u8 clk)
    {

            CR = 0;
            CH = 0;
            CL = 0;


            CMOD = (CMOD & ~0x0E) | ((clk & 7) << 1);                // PCA时钟选择, 0: 12T,  1: 2T, 2: Timer0溢出率, 3: ECI引脚输入, 4: 1T,  5: 4T,  6: 6T,  7: 8T

            PPCA = 1;        //高优先级中断

            PCA1_16bit_Timer();        //工作于16位软件定时模式
                                    //        PCA0_High_PulseEnable();        //匹配时允许高速输出
            CCAP1_tmp = 0;                //按用户需要给一个初值
            CCAP1L = 0;                        //先写CCAP0L
            CCAP1H = 0;                        //后写CCAP0H


            CR = 1;                //运行PCA定时器
    }

    //========================================================================
    // 函数: void PCA_ISR (void) interrupt PCA_VECTOR
    // 描述: PCA中断服务程序.
    // 参数: 无.
    // 返回: 无.
    // 作者: Coody
    // 版本: VER1.0
    // 日期: 2020-7-31
    // 备注:
    //========================================================================
    void PCA_ISR (void) interrupt PCA_VECTOR
    {
            if(CCF0 == 1)                //PCA模块0中断
            {
                    CCF0 = 0;                //清PCA模块0中断标志
                    if(B_M1_RunEn)
                    {
                            if(B_f1_update)        //刷新频率值
                            {
                                    B_f1_update = 0;
                                    f1_period = f1_period_set;
                            }
                            CCAP1_tmp += f1_period;
                            CCAP1L = (u8)CCAP1_tmp;                        //将影射寄存器写入捕获寄存器,先写CCAP0L
                            CCAP1H = (u8)(CCAP1_tmp >> 8);        //后写CCAP0H
                            if(P_M1_PULSE)        //产生了完整的一个脉冲
                            {
                                    if(M1_PulseCnt != 0)        // 脉冲数未完成
                                    {
                                            if(--M1_PulseCnt == 0)        //若 脉冲数-1 == 0
                                            {
                                                    B_M1_RunEn = 0;                // 关停电机
                                                    P_M1_DIR   = 1;                // 转向光耦关闭
                                                    CCAPM0 &= ~0x04;        //禁止高速输出脉冲
                                            }
                                    }
                                    if(M1_DownCnt != 0)                // 减速脉冲未完
                                    {
                                            if(--M1_DownCnt == 0)        f1_set = 200;        //设置目标频率, 开始减速
                                    }
                            }
                    }
                    else  P_M1_PULSE = 1;
            }

    }

    //========================================================================
    // 函数:u8        Timer0_Config(u8 t, u32 reload)
    // 描述: timer0初始化函数.
    // 参数:      t: 重装值类型, 0表示重装的是系统时钟数, 其余值表示重装的是时间(us).
    //       reload: 重装值.
    // 返回: 0: 初始化正确, 1: 重装值过大, 初始化错误.
    // 版本: V1.0, 2018-12-20
    //========================================================================

    u8        Timer0_Config(u8 t, u32 reload)        //t=0: reload值是主时钟周期数,  t=1: reload值是时间(单位us)
    {
            TR0 = 0;        //停止计数

            if(t != 0)        reload = (u32)(((float)MAIN_Fosc * (float)reload)/1000000UL);        //重装的是时间(us), 计算所需要的系统时钟数.
            if(reload >= (65536UL * 12))        return 1;        //值过大, 返回错误
            if(reload < 65536UL)        AUXR |= 0x80;                //1T mode
            else
            {
                    AUXR &= ~0x80;        //12T mode
                    reload = reload / 12;
            }
            reload = 65536UL - reload;
            TH0 = (u8)(reload >> 8);
            TL0 = (u8)(reload);

            ET0 = 1;        //允许中断
            TMOD = (TMOD & ~0x03) | 0;        //工作模式, 0: 16位自动重装, 1: 16位定时/计数, 2: 8位自动重装, 3: 16位自动重装, 不可屏蔽中断
            TR0 = 1;        //开始运行
            return 0;
    }



    //========================================================================
    // 函数: void timer0_ISR(void) interrupt TIMER0_VECTOR
    // 描述:  timer0中断函数.
    // 参数: none.
    // 返回: none.
    // 版本: V1.0, 2018-12-20
    //========================================================================
    void timer0_ISR(void) interrupt TIMER0_VECTOR
    {
            B_1ms = 1;        //标志1ms时隙

    }

    点评

    STC12C5A60S2的定时器不能16位自动重装,PCA的16位软件定时器用法差不多的,可以参考STC12C5A60S2的PCA程序。  详情 回复 发表于 前天 15:16
    回复 支持 反对 送花

    使用道具 举报

    该用户从未签到

    46

    主题

    3130

    回帖

    7063

    积分

    超级版主

    积分
    7063
     楼主| 发表于 前天 15:16 | 显示全部楼层
    zgjywhl 发表于 2024-6-1 10:22
    请教梁工及各位前辈大侠,我把这个移植到STC12C5A60S2,PCA0 输出10ms周期的波形,改好几天都没改明白,麻烦 ...

    STC12C5A60S2的定时器不能16位自动重装,PCA的16位软件定时器用法差不多的,可以参考STC12C5A60S2的PCA程序。
    回复 支持 反对 送花

    使用道具 举报

  • TA的每日心情
    开心
    4 天前
  • 签到天数: 1 天

    [LV.1]初来乍到

    0

    主题

    2

    回帖

    12

    积分

    新手上路

    积分
    12
    发表于 昨天 10:09 | 显示全部楼层
    感谢梁工指导,问题解决了,(差点笑出了猪叫),同时还发现我PCA两个口设置搞混了,设置的是1口和0口,测试的0口,虽然我很菜,但是会努力,会一直支持STC国产
    回复 支持 反对 送花

    使用道具 举报

    该用户从未签到

    46

    主题

    3130

    回帖

    7063

    积分

    超级版主

    积分
    7063
     楼主| 发表于 昨天 11:42 | 显示全部楼层
    zgjywhl 发表于 2024-6-2 10:09
    感谢梁工指导,问题解决了,(差点笑出了猪叫),同时还发现我PCA两个口设置搞混了,设置的是1口和0口,测 ...

    为你感到高兴!每次成功都会得到很多的经验。
    回复 支持 反对 送花

    使用道具 举报

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

    本版积分规则

    QQ|手机版|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

    GMT+8, 2024-6-3 14:23 , Processed in 0.064106 second(s), 45 queries .

    Powered by Discuz! X3.5

    © 2001-2024 Discuz! Team.

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