zoukankan      html  css  js  c++  java
  • 实现硬件PWM控制电机旋转和通过编码器计算所转圈数的简单例程

    该例程所用的硬件设备:

    直流电机驱动模块YYH-LWZ: H桥 大功率 正反转 刹车 PWM 调速 5/12/24V

    12V直流减速电机JGB37-520B:ASLONG JGB37-520B编码器减速电机直流减速马达A/B相码盘信号测速    带编码器 A/B相输出 噪音小

    芯片:IAP15w4k58s4

    电机控制:

    因该电机驱动模块无法直接通过单片机的IO口位的拉高,拉低来控制,故用PWM来控制。软件模拟PWM不够稳定快速,故采用硬件PWM,然而硬件PWM只可使用IAP15w4k58s4芯片固定的PWM输出IO口,来输出PWM波形:

    P0.6/P0.7/P1.6/P1.7/P2.1/P2.2
    P2.3/P2.7/P3.7/P4.2/P4.4/P4.5

    芯片资料:http://www.stcmcudata.com/datasheet/stc/STC-AD-PDF/STC15.pdf(要用自取)

    PWM波形输入到电机驱动模块的IO口后,被MOC管放大,在输出到电机电源线

    硬件PWM的PWM.C程序如下:

    #include "STC15W.H"        //单片机头文件
    #include "Uart.h"
    #include "PWM.h"
    
    
    /*系统晶振频率为28Mhz ,PWM输出信号频率为20khz以内*/
    //参考stc15系列单片机指南1056页,  
    
     void PWM_Init(void)
    { 
       P_SW2 |= 0x80; 
       PWMCFG = 0x00;                  //PWM的输出初始电平为低电平
       PWMCKS = 0x0f;                  //PWM的时钟为Fosc/(0+1)
       PWMC = CYCLE;                   //PWM周期,定义PWM周期(最大值为32767)
    
       PWM2CR = 0x00;                  //PWM2波形输出到P37,不使能PWM2中断   
       PWM3CR = 0x00;                  //PWM3波形输出到P21,不使能PWM3中断  
       PWM4CR = 0x00;                  //PWM4波形输出到P22,不使能PWM4中断  
       PWM5CR = 0x00;                  //PWM5波形输出到P23,不使能PWM5中断  
    
       PWM2T1 = 0x0001;               
       PWM2T2 = 0; 
               
       PWM3T1 = 0x0001;               
       PWM3T2 = 0;
                
       PWM4T1 = 0x0001;               
       PWM4T2 = 0;
                
       PWM5T1 = 0x0001;               
       PWM5T2 = 0;
        
       PWMCR |= 0x80;                  //使能PWM模块
       P_SW2 &=~0x80; 
    }
    void  IN_1( unsigned int DUTY)           //PWM2
    {   
            if(DUTY==0)                         //通过DUTY来控制占空比,进而控制PWM输出电压,最终实现转速的变化
            {
               PWMCR &=~0x01;
               PWM2=0; 
            }
            else if(DUTY==100)
            {
               PWMCR &=~0x01;
               PWM2=1; 
            }
            else
            {
               P_SW2 |= 0x80;                  //使能访问PWM在扩展RAM区的特殊功能寄存器XSFR
               PWM2T1 = 0x0001;                //设置PWM2第1次反转的PWM计数
               PWM2T2 = CYCLE * DUTY / 100;    //设置PWM2第2次反转的PWM计数
               P_SW2 &=~0x80;                  //占空比为(PWM2T2-PWM2T1)/PWMC
               PWMCR |= 0x01;                  //使能PWM信号输出
    
            }
    
    }
    void IN_2(unsigned int DUTY)                //PWM3
    {         
            if(DUTY==0)
            {
               PWMCR &=~0x02;
               PWM3=0; 
            }
            else if(DUTY==100)
            {
               PWMCR &=~0x02;
               PWM3=1; 
            }        
            else
            { 
               P_SW2 |= 0x80;          
               PWM3T1 = 0x0001;                
               PWM3T2 = CYCLE * DUTY / 100;                                     
               P_SW2 &=~0x80;                 
               PWMCR |= 0x02;                   
    
            }        
    }
    void IN_3(unsigned int  DUTY)                  //PWM4
    {         
             if(DUTY==0)
            {
              PWMCR &=~0x04;
              PWM4=0; 
            }
            else if (DUTY==100)
            {
              PWMCR &=~0x04;
              PWM4=1; 
            }
            else
            {         
              P_SW2 |= 0x80;                 
              PWM4T1 = 0x0001;               
              PWM4T2 = CYCLE * DUTY / 100;                                
              P_SW2 &=~0x80;                  
              PWMCR |= 0x04;                   
    
            }
    }
    
    
    void IN_4(unsigned int  DUTY)                  //PWM5
    {         
             if(DUTY==0)
            {
               PWMCR &=~0x08;
               PWM5=0; 
            }
            else if (DUTY==100)
            {
               PWMCR &=~0x08;
               PWM5=1; 
            }
            else
            {         
               P_SW2 |= 0x80;                 
               PWM5T1 = 0x0001;               
               PWM5T2 = CYCLE * DUTY / 100;                                
               P_SW2 &=~0x80;                  
               PWMCR |= 0x08;                   
    
            }
    }
    
    
    //功能:电机驱动模块的输入端口控制函数   
    void IN_SetPwm(int wide_1,int wide_2,int wide_3,int wide_4,int uDir)
    {
       if(uDir==1)
       {
         IN_1(wide_1);
         IN_2(wide_2);
         IN_3(wide_3);
         IN_4(wide_4);
       }
    }

     硬件PWM的PWM.h程序如下:

    #ifndef _PWM_H_
    #define _PWM_H_
    
    #include "STC15W.H"    
    
    
     //芯片晶振频率设置为28mhz
    
    #define CYCLE   0x6500L     //定义PWM周期(最大值为32767)
            
    sbit PWM2=P3^7;                                         
    sbit PWM3=P2^1;                                           
    sbit PWM4=P2^2;                                        
    sbit PWM5=P2^3;
    
    
    
    extern void PWM_Init(void);extern void IN_SetPwm(int wide_1,int wide_2,int wide_3,int wide_4,int uDir);
    
    
     #endif

     编码器计数:

    该电机自带的编码器为A/B相霍尔计数编码器,根据编码器的旋转产生A,B相的不同方波,每有A相的4个方波,编码器转了90度,转一圈故有12个方波信号。根据旋转方向的不同,A波产生的上升下降沿时,B波同时刻处于不同的电平。电机输出轴转一圈的时间内,根据电机的转速,减速比和PWM的频率不同,编码器所转的圈数是不固定的,要精确计数要使用算法,该例程只是前提量不变的估量值。

    编码器的encoder.c程序:

    #include "STC15W.H"        //单片机头文件
    #include "Uart.h"
    #include "encoder.h"
    #include "center.h"
    
    unsigned char Last_io=0;     
    unsigned char Curr_io=0;
    
    
    
     //编码器结构体的初始化
    void Encoder_Init(Encoder_HandleTypeDef * encoder)
    {
        encoder-> zheng_count =0;
        encoder-> fan_count   =0;
        encoder-> end_count   =0;
    
    }
    
    
    
    
    void Delay50us()        //@28MHz
    {
        unsigned char i, j;
    
        i = 2;
        j = 89;
        do
        {
            while (--j);
        } while (--i);
    }
    
    
    
    
     void Initial_INT0(void)      //用外部中断来实现A波的触发
    {    
           
        IT0=0;      // 设置成上升沿和下降沿均触发  
        EX0=1;      //使能INT0中断
        EA=1;   
        
    }
    
    int exint0() interrupt 0  //外部中断入口
    
    {  
        Delay50us();    
        if(PIN_A==1)          //上升沿触发
        {   
         
          Curr_io=PIN_B;      //记录PIN_B的触发信号
            
        }
        else if(PIN_A==0)     //下降沿触发
        {   
         
          Last_io=PIN_B;      //记录PIN_B的触发信号
               
            
        }
    
    }
    
    
    //扫描编码器的计数
    void scan_encoder(Encoder_HandleTypeDef *encoder)
    {
       
    
            if((Curr_io==1)&&(Last_io==0))       //编码器逆时针旋转时,A波上升沿时,B波为1,A波下降沿时,B波为0;
            {
                 
                 encoder->zheng_count++;         //每有12个判断信号,编码器转一圈
    
                 SendString(" 证 转 \n");     
    
                 Curr_io=0;                         // 判断信号置0,如果不置0会有误差
                 Last_io=0;    
        
            }
           if((Curr_io==0)&&(Last_io==1))        //编码器顺时针旋转时,A波上升沿时,B波为0,A波下降沿时,B波为1;
           {
           
                 encoder->fan_count++;             //每有12个判断信号,编码器转一圈
    
                 SendString(" 反 转  \n");
                                          
                 Curr_io=0;                         // 判断信号置0
                 Last_io=0;    
                
            }    
       
        if(encoder->zheng_count==360*15)         //当编码器所转圈数到达一定数量时,电机的输出轴转一圈,该数字为估量值
        {
              
            //  SendString("输出轴 证 转 了 一  圈\n");      
              
              encoder->zheng_count=0;
    
              encoder->end_count++;
              
        }    
        if(encoder->fan_count==360*15)
        {
              
         //       SendString("输出轴 反 转 了 一  圈\n");          
             
             encoder->fan_count=0;
             
             encoder->end_count++;                  
             
        }    
        
             
    }

    编码器的encoder.h程序:

    #ifndef _ENCODER_H_
    #define _ENCODER_H_
    
    #include "STC15W.H"    
    
     sbit PIN_B=P4^1;               //B相接P41
     sbit PIN_A=P3^2;               //A相接外部中断使能端口P41
    
    
    
    
     typedef struct Encoder         //编码器结构体
    {
    
      unsigned int zheng_count;     //编码器正转圈数            
      unsigned int fan_count;       //编码器反转圈数
      unsigned int end_count;       //输出轴已转圈数
          
    }Encoder_HandleTypeDef;          
    
    
    extern void scan_encoder(Encoder_HandleTypeDef *motor);
    extern void Encoder_Init(Encoder_HandleTypeDef * encoder);
    extern void Initial_INT0(void);
    
    
     #endif

    电机控制:

    控制电机正转或反转,并旋转指定圈数

    控制电机的motor.c程序 

    #include "STC15W.H"        //单片机头文件
    #include "Uart.h"
    #include "encoder.h"
    #include "PWM.h"
    #include"motoc.h"
    
    
    
    
    void Motor_Init(Motor_HandleTypeDef *motor)
    {
           
        motor->H_PWM       =0;
        motor->L_PWM       =0;
        motor->number       =0;
        motor->flag           =1;
    }
    
                                                                                       
    void Motor_Start(Motor_HandleTypeDef *motor,int tack)                            //启动电机
    {                                                                            
       motor->H_PWM=20;                                                                //占空比恒为20%
       if(tack==1)
       {
    
            IN_SetPwm(motor->H_PWM, motor->L_PWM, motor->L_PWM, motor->H_PWM,1);    //MOC管显示为 (1 0 0 1)     电机正转
            
       }
       if(tack==2)
       {
        
            IN_SetPwm(motor->L_PWM, motor->H_PWM, motor->H_PWM, motor->L_PWM,1);    //MOC管显示为 (0 1 1 0)     电机反转
        
       }
    
    }
    
    void Motor_Stop(Motor_HandleTypeDef *motor,int tack)                            //关闭电机
    { 
       motor->H_PWM=0;
       if(tack==1)
       {       
             
            IN_SetPwm(motor->H_PWM,motor->H_PWM,motor->L_PWM,motor->L_PWM,1);        //MOC管显示为 (0 0 0 0)     电机刹车
       }
       if(tack==2)
       {
            
            IN_SetPwm(motor->L_PWM,motor->L_PWM,motor->H_PWM,motor->H_PWM,1);        //MOC管显示为 (0 0 0 0)     电机刹车
       }
    }
    
    
    //电机开关函数
    void Motor_key(Motor_HandleTypeDef *motor)
    
    {
           if( motor->flag==0)
        {
            motor->flag=1; 
        }
    
    
    }
    
    
    
    //tack:电机方向       count:目的圈数
    void Motor_control(Motor_HandleTypeDef *motor,Encoder_HandleTypeDef * encoder,int tack,int count)
    {
     
           if(motor->flag==1)                         //flag=1时,电机才能运行
           {
             Motor_Start(motor,tack);                 //启动电机
    
             motor->number=encoder->end_count ;
    
             if(motor->number >= count)                 //当输出轴转的圈数到达目的圈数时,停止旋转
             {
                 Motor_Stop(motor,tack);             //关闭电机
    
                 encoder->end_count=0;                 //编码器圈数置0
    
                 motor->flag=0;                         //flag=0
    
                 motor->number=0;                     //电机圈数置0
             }
    
           }
    }

     控制电机的motor.h程序

    #ifndef _MOTOR_H_
    #define _MOTOR_H_
    
    #include "STC15W.H"    
    
                                       
    
    typedef struct Motor          //电机结构体
    {
     
      int L_PWM;                 //PWM低电位
      int H_PWM;                 //PWM高电位
      int number;                 //电机已转圈数
      int flag;                     //电机开关
                         
    }Motor_HandleTypeDef;
    
    
    
     extern  void Motor_Init(Motor_HandleTypeDef  *motor);
     extern  void Motor_key(Motor_HandleTypeDef *motor);
     extern  void Motor_Start(Motor_HandleTypeDef *motor,int tack);
     extern  void Motor_Stop(Motor_HandleTypeDef *motor,int tack);
     extern  void Motor_control(Motor_HandleTypeDef *motor,Encoder_HandleTypeDef * encoder,int tack,int count);

    主函void main()

    {    
       Motor_HandleTypeDef   motor;
       Encoder_HandleTypeDef encoder;    
      
    
       STC15W_IOinit();          //单片机初始化    
       Core_Init_Uart();         //串口初始化
       PWM_Init();               //PWM初始化
       Encoder_Init(&encoder);   //编码器初始化
       Motor_Init(&motor);       //电机初始化
       Initial_INT0();           //外部中断初始化
    
    
    
        while(1)
        {
           Motor_control(&motor,&encoder,1,5);
           scan_encoder(&encoder);
       
       }
    
    
     }

    结语

    只不过一个简简单单的控制电机和计算圈数的程序,就前前后后花了我两个多星期的业余时间。现在看来,单片机有很多硬件功能是我了解不足的,差不多是从零开始写的。学不以致用不可取以。期望对后来者有参考帮助。

    看官们觉得好,有用就给个推荐,如果何处不足,有错请大方留言指出。

    谢谢浏览。

  • 相关阅读:
    通过静态广播监听网络变化,在通过回调通知
    支付宝九宫格
    toolBar
    QQ发送位置(高德地图)
    聊天消息 左右item
    Scrollview 嵌套 RecyclerView 及在Android 5.1版本滑动时 惯性消失问题
    判断软键盘是否弹出 并且隐藏
    Solve Longest Path Problem in linear time
    Why longest path problem doesn't have optimal substructure?
    [leetcode] Reorder List
  • 原文地址:https://www.cnblogs.com/feiniaoliangtiangao/p/10088748.html
Copyright © 2011-2022 走看看