在标准I / O端口(ATmega32 AVR)上进行多次伺服控制?

时间:2015-10-04 10:14:16

标签: embedded avr atmega

当按下特定按钮时,我基本上尝试对多个伺服电机执行操作。以下代码工作正常但只能在多个伺服电机上执行单个移动,即当按下按钮Servo1移动到900,伺服2移动到1500,否则它们会回到标准位置。

我想要实现的是一组动作,例如当按下按钮时我想要两个舵机摆动(0到180到0(3个不同的动作))。我尝试使用延迟功能并更改伺服的值但是它不起作用,我假设这是因为延迟变得大于我的快速PWM时间段。

void main()
{
DDRB = 0xFF;
DDRC = 0x00;
PORTC = 0XFF;

TCCR1A |= 1<<WGM11;
TCCR1B |= 1<<WGM12 | 1<<WGM13 | 1<<CS10;
TIMSK  |= 1<<OCIE1A;

ICR1=19999; 

sei();

uint16_t Servo1 = 2000, Servo2 = 900;

while(1)
{
    if(bit_is_clear(PINC,0)) 
  {
      Servo1 = 900, Servo2 = 1500;
     }
 else{
     Servo1 = 1500, Servo2=2200;
 }
  if(TCNT1>=800 && TCNT1<=2400)
  {
       if (TCNT1 >= Servo1 && bit_is_set(PORTB,PINB0)) PORTB &= ~(1<<PINB0);
       if (TCNT1 >= Servo2 && bit_is_set(PORTB,PINB1)) PORTB &= ~(1<<PINB1); 
  } 

}
}

ISR(TIMER1_COMPA_vect)
{
 PORTB = 0xFF;  
}

我该怎么做? 任何帮助表示赞赏。

1 个答案:

答案 0 :(得分:0)

您似乎正在使用Timer1手动实现PWM,这意味着您无法添加来自&lt; util / delay.h&gt;的延迟。所以,你唯一的选择是实现类似于状态机的东西。

为状态定义一个变量,其中STATE0将是等待状态,STATE1将是第一个移动STATE2,第二个移动等等......

然后定义一个'时间表'来定义它在每个状态上等待的时间,(理想情况下它应该是一个条件表,但无论如何......)

enum{ STATE0,STATE1,STATE2};// from 0 to 2
int time[3]={0,500,500}; //for state 0 it'll wait the button
volatile int time_counter=0; //needs to be volatile 'cos it's used inside the interrupt
int state = STATE0;

void main()
{
DDRB = 0xFF;
DDRC = 0x00;
PORTC = 0XFF;

TCCR1A |= 1<<WGM11;
TCCR1B |= 1<<WGM12 | 1<<WGM13 | 1<<CS10;
TIMSK  |= 1<<OCIE1A;

ICR1=19999; 

sei();

uint16_t Servo1 = 2000, Servo2 = 900; // are these initial values ok for STATE0?

while(1)
{
    //eval state
    switch(state){
        case STATE0:
            if(bit_is_clear(PINC,0)) // if button
            {
                //change state
                state=STATE1;
                time_counter=0;
                //do task
                Servo1 = 900, Servo2 = 1500;
            }
        break;
        case STATE1:
            if(time_counter>time[state])
            {
                //change state
                state=STATE2;
                time_counter=0;
                //do task
                Servo1 = 1500, Servo2 = 2200;
            }
        break;
        case STATE2:
            if(time_counter>time[state])
            {
                //change state
                state=STATE0; // go to wait the buton again
                time_counter=0; // not necesary but anyway...
                //do task
                Servo1 = 2000, Servo2 = 900;
            }

        break;
    }
    //do the pwm stuff
    if(TCNT1>=800 && TCNT1<=2400)
  {
       if (TCNT1 >= Servo1 && bit_is_set(PORTB,PINB0)) PORTB &= ~(1<<PINB0);
       if (TCNT1 >= Servo2 && bit_is_set(PORTB,PINB1)) PORTB &= ~(1<<PINB1); 
  } 

}
}

ISR(TIMER1_COMPA_vect)
{
 PORTB = 0xFF;  
 time_counter++; //here we count the pwm cycles...
}

我没有检查你的计时器配置,因此我实际上不知道500个循环的等待时间是否应该足够或太多,但你应该根据你的测试改变时间[]表...

还建议使用2个开关,一个用于检查条件并相应地更改状态,另一个用于实际执行实际状态的任务...我只是懒得并且在一个开关中都做了。 ..

同样在使用枚举{}时,最好将状态重命名为soemthing,如STATE_WAIT或STATE_POS_180或STATE_LOCATION_A ......