zoukankan      html  css  js  c++  java
  • 一个基于ATMEGA128的直流电机抱死程序(转)

    源:一个基于ATMEGA128的直流电机抱死程序

        先说一下我的硬件情况:一块ATMEGA128实验板;一个带编码器的80:1的变速电机,编码器的输出端连接到单片机的PD4和PD5引脚;一块电机驱动电路,该电路的输入为:24v电源、两路pwm信号输入,输出即为电机的正负极,要用该电路来驱动电机,则必须让两路pwm输入信号的一路占空比为0,另一路不为0,相当于让电机的一极接地,另一极接pwm,通过控制两路pwm的占空比来控制电机的转速和转动方向。pwm信号的输入端连接到单片机的PD6和PD7引脚。

        下面是我的程序的设计思路: 这个程序用了两个定时器:timer0和timer1。

        timer0用来产生pwm。timer0产生pwm信号是这样实现的:程序中有一个timer0的溢出事件计数器,和两个保存两路pwm信号占空比的变量,当timer0溢出事件计数器计数超过100时,如果某个pwm信号占空比不为0,则把相应pwm引脚置高电平,同时清零此计数器,当此计数器等于某个占空比时,则把相应引脚置低电平,从而实现timer0溢出事件计数器从0计数到100时输出一个周期的pwm信号。通过调节timer0的溢出频率,即可调节pwm信号输出的频率。 

         timer1用来对编码器的输出进行计数,同时调整pwm的占空比,实现对电机的控制。对编码器的输出计数是利用了timer1的输入捕捉功能,由于电机可以正转,也可以反转,导致编码器的CHA和CHB的输出也不同,所以可以在程序中可以判断电机是正转还是反转,再对编码器的输出脉冲进行计数,当电机正转的时候计数增加,电机反转的时候计数减少,所以编码器的计数值是有正负的从而可以知道什么时候该通过调整pwm来控制电机。

        下面是我的调试过程,也算是一点经验吧:以开始的思路是只要编码器的计数值不为0,我就要让电机反方向转动,以保持电机抱死,发送给电机的pwm是固定的数值,但是这样反而是抱不死,它在前后地抖动,而且pwm的占空比越大,电机抖动得越厉害,这样显然不行;后来想了一个办法,就是如果编码器的计数值在一定的范围内,我就不用让电机反方向转动。因为这个电机是变速电机,如果电机里面只转动一点点,在外面看来就相当于不动,这样的话就给电机预留了一部分转动的空间,用来消除抖动就是说在这个空间内是不发送pwm给电机的,或者说电机两极的pwm占空比都为0。这样一来,当pwm占空比比较低时,是可以消除抖动,但是力气不大,就是说还是可以用钳子拧得动,调了很久都无法在抖动和电机力气之间取得平衡。后来又想了一个办法,在这个基础上再改进,因为之前的pwm占空比都是不变的,所以很难达到令人满意的效果,现在的方法是,根据电机被拧动的角度,或者说编码器的计数值大小来调整pwm的占空比,编码器的计数值偏离0越多(正或负得越大),pwm的占空比就越大,电机的力气也就越大,从而不会出现电机一旦被拧动就马上以最大速度转回去的情况,抖动也就消除了,而且电机力气很大。

    编译环境是AVR Studio 5.0,下面是程序代码:

    #include <avr/io.h>
    
    #include <avr/interrupt.h> 
    
     
    
     
    
    int forward = 0, reverse = 0;//存储电机正转和反转pwm占空比的变量
    
    int timer0_count = 0;//timer0溢出事件计数器
    
    int capt_count = 0;//输入捕捉事件计数器
    
     
    
     
    
    void port_init(void)
    
    {
    
    PORTA = 0x00;
    
    DDRA  = 0x00;
    
    PORTB = 0x00;
    
    DDRB  = 0x00;
    
    PORTC = 0x00; //m103 output only
    
    DDRC  = 0x00;
    
    PORTD = 0x00;
    
    DDRD  = 0xC0;
    
    PORTE = 0x00;
    
    DDRE  = 0x00;
    
    PORTF = 0x00;
    
    DDRF  = 0x00;
    
    PORTG = 0x00;
    
    DDRG  = 0x00;
    
    }
    
     
    
     
    
    void timer0_init(void)
    
    {
    
    TCCR0 |= 5;//256分频,普通模式
    
    TIMSK |= 0x01;//timer0溢出中断
    
    TCNT0 = 0xFE;//TCNT0赋初值
    
    }
    
     
    
     
    
    void timer1_init(void)
    
    {
    
    TCCR1B = 0x00;//停止
    
    TCCR1A = 0x00;//普通模式
    
    TCCR1C = 0x00;
    
    TCNT1 = 0;//计数初值
    
    TCCR1B = 0xC4;//启动定时器,256分频,使能输入捕捉噪声抑制器,输入捕捉触发沿选择:上升沿
    
    TIMSK = 0x24;//输入捕捉中断使能,T/C1溢出中断使能
    
    }
    
     
    
    /************************************************************************/
    
    /*  timer0溢出中断函数,产生提供给电机的pwm                             */
    
    /************************************************************************/
    
    ISR(TIMER0_OVF_vect<span style="color: rgb(255, 0, 0);">)//200kHz
    
    </span>{
    
    TCNT0 = 0xFE;//TCNT0重新赋值
    
    //当timer0_count等于100时,如果正转或反转的占空比不为0,则相应引脚输出高电平
    
    if(++timer0_count >= 100)//<span style="color: rgb(255, 0, 0);">对timer0溢出事件计数100次,相当于100分频,最后输出到电机的pwm频率是2kHz
    
    </span>{
    
    timer0_count = 0;
    
    if(forward != 0<span style="color: rgb(255, 0, 0);">)//forward, reverse存储电机正转和反转pwm占空比的变量
    
    </span>{PORTD |= (1<<6);}
    
    if(reverse != 0)
    
    {PORTD |=(1<<7);}
    
    }
    
    //<span style="color: rgb(255, 0, 0);">当timer0_count等于正转或反转的占空比时,相应引脚输出低电平,实现输出pwm信号
    
    </span>if(timer0_count == forward)
    
    {PORTD &= ~(1 << 6);}
    
    if(timer0_count == reverse)
    
    {PORTD &= ~(1 << 7);}
    
    }
    
     
    
    /************************************************************************/
    
    /*  timer1输入捕捉中断函数,对编码器输出的上升沿进行计数              */
    
    /************************************************************************/
    
    ISR(TIMER1_CAPT_vect)
    
    {
    
      if(PIND & (1 << 5))//电机反转
    
      {capt_count--;}//输入捕捉计数器减1
    
      else               //电机正转
    
      {capt_count++;}//输入捕捉计数器加1
    
    }
    
     
    
     
    
    /************************************************************************/
    
    /*  <span style="color: rgb(255, 0, 0);">timer1溢出中断函数,100Hz,用于调整电机转速和转动的方向,实现电机抱死</span>*/
    
    /************************************************************************/
    
    ISR(TIMER1_OVF_vect)
    
    {
    
    TCNT1 = 64910;                           //重新给TCNT1赋值
    
    static unsigned char <span style="color: rgb(255, 0, 0);">motor_state = 0;    //电机的状态,标志电机是正转还是反转,0:正转,1:反转
    
    </span>switch(motor_state)
    
    {
    
    case 0<span style="color: rgb(255, 0, 0);">://电机正转时
    
    </span>if(capt_count > 40)              <span style="color: rgb(255, 0, 0);">//如果编码器正转计数超过40,则电机需要反转,以保持电机不动
    
    </span>{reverse = capt_count - 40;} //直接把编码器计数值减去40,作为反转的占空比
    
    else if(capt_count < 0)          <span style="color: rgb(255, 0, 0);">//如果编码器计数值小于0
    
    </span>{motor_state = 1;}           //进入状态1
    
    else                            <span style="color: rgb(255, 0, 0);"> //如果编码器计数值在0~40内,为了不发生抖动,不需要反转
    
    </span>{reverse = 0;}               //反转的占空比为0,相当于负极接地
    
    forward = 0;                     //正转的占空比为0,相当于正极接地
    
    break;
    
     
    
    case 1:
    
    if(capt_count < -40)             <span style="color: rgb(255, 0, 0);">//如果编码器反转计数超过40,则电机需要正转</span>,以保持电机不动
    
    {forward = (-capt_count) - 40;}//直接把编码器计数值减去40,作为正转的占空比
    
    else if(capt_count > 0)          <span style="color: rgb(255, 0, 0);">//如果编码器计数值大于0
    
    </span>{motor_state = 0;}           //返回状态0
    
    else                             <span style="color: rgb(255, 0, 0);">//如果编码器计数值在-40~0内,为了不发生抖动,不需要正转
    
    </span>{forward = 0;}               //正转的占空比为0,相当于正极接地
    
    reverse = 0;                     //反转的占空比为0 ,相当于负极接地
    
    break;
    
     
    
    default:
    
    break;
    
    }
    
    }
    
     
    
     
    
    void Init_Devices(void)
    
    {
    
    cli();//关闭全局中断
    
    port_init();//I/O口初始化
    
    timer1_init();//定时/计数器1初始化
    
    timer0_init();//计时/计数器0初始化
    
    sei();//打开全局中断
    
    }
    
     
    
     
    
    int main(void)
    
    {
    
        Init_Devices();
    
        while(1)
    
        {}
    
     
    
        return 0;
    
    }
  • 相关阅读:
    English,The Da Vinci Code, Chapter 23
    python,meatobject
    English,The Da Vinci Code, Chapter 22
    English,The Da Vinci Code, Chapter 21
    English,The Da Vinci Code, Chapter 20
    English,The Da Vinci Code, Chapter 19
    python,xml,ELement Tree
    English,The Da Vinci Code, Chapter 18
    English,The Da Vinci Code, Chapter 17
    English,The Da Vinci Code, Chapter 16
  • 原文地址:https://www.cnblogs.com/LittleTiger/p/4595880.html
Copyright © 2011-2022 走看看