Pages: [1]   Go Down
Author Topic: [RESOLU] ServoMoteurs + CTC (ou PWM) AnyPins  (Read 845 times)
0 Members and 1 Guest are viewing this topic.
0
Offline Offline
God Member
*****
Karma: 0
Posts: 798
Arduino rocks
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Yep!

Ce week-end, j'ai commencé à tester mes servomoteurs avec plus ou moins de réussite.

J'ai donc commencé par un classique, la modul de largeur d'impulsion (pwm) et çà marche pas mal. Mais comme j'aimerais utiliser un attiny2313 comme controlleur, il faut que je puisse utiliser tous les pins disponibles, y compris les non-pwm.

Alors, j'ai cherché sur la toile et j'ai effectivement trouvé des choses, mais en assembleur, et c'est vite devenu pour moi indigeste  smiley-mr-green

J'effectue mes essais avec 2 servos et un atmega328 (duemilanove). J'utilise pour contrôler mes servos le timer 1 en mode CTC afin de simuler les 50hz indispensables.
Le problème est que le second servo ne réagit pas du tout comme il faudrait (ne tourne que dans un sens avec des ratés de temps à autre, alors que le premier servo est ok) et j'ai l'impression qu'entre la commande (séquentielle) et la pulsation, j'ai un léger décalage temporel qui me fait sauter les pulsations suivantes.

Je pense être dans la bonne direction mais là je sèche sur la méthode pour résoudre ce problème. Un peu d'aide ferait du bien smiley-wink

The code :

Code:
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

#define Wait(T) _delay_ms(T)

volatile unsigned int servo[9] = {1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 6000};

//----------------------------------------------------------------

ISR(TIMER1_COMPA_vect)
{
  static byte servo_num;
  
  PORTB = (1 << servo_num);
  OCR1A = servo[servo_num];
  servo_num++;
  if(servo_num > 7) servo_num = 0;
}

ISR(TIMER1_OVF_vect)
{

}

void setup()
{
  _init_();
}

void loop()
{

  servo[0] = 1000;          // servos 0 et 1 gauche
  servo[1] = 1000;
  Wait(2000);                 // marche pas

  servo[0] = 1500;          // servos 0 et 1 centre
  servo[1] = 1500;
  Wait(2000);                // marche pas

  servo[0] = 2000;          // servos 0 et 1 droite
  servo[1] = 2000;
  Wait(2000);                   // marche pas

}

void _init_()
{
  //cli();
  
  TCCR1A = 0;
  TCCR1B = 0;
  
  DDRB |= B00000011; // PB0 et PB1
  __asm__("nop\n\t");
 
  //TCCR1B |= (1 << WGM12) | (1 << CS11);
  TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS11); // CTC mode 14, prescaler de 8
  TIMSK1 |= (1<<OCIE1A) | (1 << TOIE1); // Overflow et comparateur interrupt
  
  TCNT1 = 0;
  ICR1 = 20000; // 20000 µS = 20 ms = 50 hz
  
  sei();
}

EDIT 1 : 2 précisions, en 1, ce sont des servos 360°, en deux, la fonction _delay_ms ne semble pas fonctionner.

Merci.

@+

Zoroastre.
« Last Edit: July 17, 2012, 01:49:41 pm by zoroastre » Logged

Veuillez indiquer [RESOLU] dans l'entête du titre en éditant votre premier message smiley-wink

Britanny
Offline Offline
Full Member
***
Karma: 0
Posts: 233
Mais vrai, j'ai trop pleuré, toute lune est atroce
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

 smiley-fat
huuuuuuu.

j'ai pour ma part joué avec des attiny84 et un bootloader arduino.
en cherchant j'ai trouvé une version plus légère des lib arduino standarts pour compiler.
j'avais trouvé ça: http://code.google.com/p/arduino-tiny/
et ça: https://github.com/damellis/attiny

la première devrait t'intéresser davantage, elle gère le 2313.

Par ailleurs avec ces versions, il faut utiliser "SerialServo" et plus la lib "servo" avec un rafraichissement tous les 40 ou 50 ms...
de gère un seul servo et ça a très bien fonctionné.

Bon ces lib ne gèrent que des servos 180° alors je sais pas si ça t'aidera vraiment.

Par simple curiosité:
tu les trouves où les servos à 360 ?
Ce sont des servos hackés pour faire un suiveur de lignes? (le servo sert de moteur?)

en espérant que ça t'aidera...
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 22
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Salut,

Quote
Bon ces lib ne gèrent que des servos 180° alors je sais pas si ça t'aidera vraiment.

Les servo-moteurs 360° peuvent se piloter comme des servos normaux ; donc avec la librairie Servo, mais en prenant ces valeurs :
 - '0' : Marche arrière vitesse maximum
 - '90' : Arrêt
 - '180' : Marche avant vitesse maximum

Bonne chance !
Logged

Lao Tseu l'a dit "Longue est la route qui mène à la connaissance, et nombreux sont les péages".

Britanny
Offline Offline
Full Member
***
Karma: 0
Posts: 233
Mais vrai, j'ai trop pleuré, toute lune est atroce
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

ok je connais alors... smiley-kiss
je savais pas comment ça s'appelait smiley-wink
il faut ajuster les valeurs basses et hautes des valeurs d'attach pour avoir un stop à 90°.
merci.
Logged

0
Offline Offline
God Member
*****
Karma: 0
Posts: 798
Arduino rocks
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Yep!

Merci pour vos commentaires messieurs  smiley-eek-blue

Pour info, j'ai déjà joué avec des tiny2313 et je possède de longue date le core  smiley-lol

Comme je n'arrive pas à avoir le résultat escompté avec le mode CTC, je suis retourné sous PWM pour valider mes essais.

J'arrive donc à piloter mes servos par ce biais. Par contre, je dois toujours avoir un problème de timing, les servos n'ont pas leur vitesse maxi et on entends les pulsations à l'oreille, c'est donc un peu beaucoup saccadé.

Et un nouveau code, un :

Code:
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>


volatile byte s;
volatile unsigned int servoPulse[8]={3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000};

void _init_();

void setup()
{
  Serial.begin(19200);
  _init_();
}

void loop()
{
  if (Serial.available())
  {
    char value = Serial.read();
    switch(value)
    {
      case 's' : stopPwm(); Serial.println("stop"); break;
      case 'd' : startPwm(); Serial.println("start"); break;
      
      case 'a' : servoPulse[0] = 1800; break; // fast
      case 'z' : servoPulse[0] = 4200; break; // fast
      case 'e' : servoPulse[0] = 3000; break; // stop
      
      case 'f' : servoPulse[0] = 2750; break; // slow
      case 'r' : servoPulse[0] = 3250; break; // slow
      
      case 'w' : servoPulse[1] = 1800; break; // fast
      case 'x' : servoPulse[1] = 4200; break; // fast
      case 'c' : servoPulse[1] = 3000; break; // stop
      
      case 'v' : servoPulse[1] = 2750; break; // slow
      case 'b' : servoPulse[1] = 3250; break; // slow
      
      
      default : break;
    }
  }
  
  servoPulse[0] = 1800;
  servoPulse[1] = 1800;
  _delay_ms(1000);
  servoPulse[0] = 3000;
  servoPulse[1] = 2700;
  _delay_ms(1000);
  servoPulse[0] = 4100;
  servoPulse[1] = 2000;
  _delay_ms(1000);
  
}

void _init_()
{
  
  cli();
  
  TCCR1A = 0;
  TCCR1B = 0;
  
  DDRB |= B00000011; // PB0, PB1 = Servos
   __asm__("nop\n\t");
  
  TCCR1A = (1 << WGM11); // Fast PWM, non invert
  TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS11); // prescaler = 8
  
  TIMSK1 |= ((1<<OCIE1A) | (1<<TOIE1));
  
  TCNT1 = 0;
  
  ICR1 = 39999; // ((16000000 / 50) / 8) - 1 = 40000 - 1
  OCR1A = 3000; // center position (40000 x 1.5)/20 = 3000
  
  sei();
  
}

ISR(TIMER1_COMPA_vect)
{
   PORTB &= ~( 1 << s);      //Turn off PBx
   s++;                      //Increment Servo Channel
   if (s > 7) s = 0;
   OCR1A = servoPulse[s];      //Update PWM duty for next Servo
}

ISR(TIMER1_OVF_vect)
{
   PORTB |= ( 1 << s);      //Turn on Servo Channel (s)
}

void stopPwm()
{
  TCCR1A = 0x0;
}

void startPwm()
{
  TCCR1A = (1 << WGM11) | (1 << COM1A1);
}


En simplifiant ce code pour un seul servo, çà marche impec  smiley-neutral

Code:
ISR(TIMER1_COMPA_vect)
{
  PORTB &= ~(1<<PB0);
}

ISR(TIMER1_OVF_vect)
{
  PORTB |= (1<<PB0);
}

@+

Zoroastre.
« Last Edit: July 17, 2012, 01:35:19 pm by zoroastre » Logged

Veuillez indiquer [RESOLU] dans l'entête du titre en éditant votre premier message smiley-wink

0
Offline Offline
God Member
*****
Karma: 0
Posts: 798
Arduino rocks
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Yep!

P*ta$n, je suis coN des fois...

ICR1 = 40000 soit 20 ms pour 1 servo, mais pour 8, ben faut diviser par 8 = 5000.

C'est en écrivant que je me suis rendu compte de ma connerie  smiley-mr-green

Code:
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>


volatile byte s;
volatile unsigned int servoPulse[8]={3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000};

void _init_();

void setup()
{
  Serial.begin(19200);
  _init_();
}

void loop()
{
  if (Serial.available())
  {
    char value = Serial.read();
    switch(value)
    {
      case 's' : stopPwm(); Serial.println("stop"); break;
      case 'd' : startPwm(); Serial.println("start"); break;
      
      case 'a' : servoPulse[0] = 1800; break; // fast
      case 'z' : servoPulse[0] = 4200; break; // fast
      case 'e' : servoPulse[0] = 3000; break; // stop
      
      case 'f' : servoPulse[0] = 2750; break; // slow
      case 'r' : servoPulse[0] = 3250; break; // slow
      
      case 'w' : servoPulse[1] = 1800; break; // fast
      case 'x' : servoPulse[1] = 4200; break; // fast
      case 'c' : servoPulse[1] = 3000; break; // stop
      
      case 'v' : servoPulse[1] = 2750; break; // slow
      case 'b' : servoPulse[1] = 3250; break; // slow
      
      
      default : break;
    }
  }
  
/*  
  servoPulse[0] = 1800;
  servoPulse[1] = 1800;
  _delay_ms(1000);
  servoPulse[0] = 3000;
  servoPulse[1] = 2700;
  _delay_ms(1000);
  servoPulse[0] = 4100;
  servoPulse[1] = 2000;
  _delay_ms(1000);
*/

}

void _init_()
{
  
  cli();
  
  TCCR1A = 0;
  TCCR1B = 0;
  
  DDRB |= B00000011; // PB0, PB1 for the test (normal pin mode)
  __asm__("nop\n\t");
  
  TCCR1A = (1 << WGM11); // Fast PWM, non invert
  TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS11); // prescaler = 8
  
  TIMSK1 |= ((1<<OCIE1A) | (1<<TOIE1)); // match compare et overflow interrupts
  
  TCNT1 = 0;
  
  ICR1 = 5000; // ((16000000 / 50) / 8) - 1 = 40000, 40000 / 8 pulses = 5000 (max servo pulse = 4100)
  OCR1A = 3000; // center position (40000 x 1.5)/20 = 3000
  
  sei();
  
}

ISR(TIMER1_COMPA_vect)
{
   PORTB &= ~( 1 << s);      //Turn off PBx
   s++;                      //Increment Servo Channel
   if (s > 7) s = 0;
   OCR1A = servoPulse[s];      //Update PWM duty for next Servo Channel
}

ISR(TIMER1_OVF_vect)
{
   PORTB |= ( 1 << s);      //Turn on Servo Channel (s)
}

void stopPwm()
{
  TCCR1A = 0x0;
}

void startPwm()
{
  TCCR1A = (1 << WGM11);
}

@+

Zoroastre.
« Last Edit: July 17, 2012, 04:14:40 pm by zoroastre » Logged

Veuillez indiquer [RESOLU] dans l'entête du titre en éditant votre premier message smiley-wink

Pages: [1]   Go Up
Jump to: