[RESOLU] ServoMoteurs + CTC (ou PWM) AnyPins

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 :grin:

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 :wink:

The 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.

:drooling_face:
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: Google Code Archive - Long-term storage for Google Code Project Hosting.
et ça: GitHub - damellis/attiny: ATtiny microcontroller support for the Arduino IDE

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...

Salut,

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 !

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

Yep!

Merci pour vos commentaires messieurs :fearful:

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

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 :

#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 :expressionless:

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

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

@+

Zoroastre.

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 :grin:

#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.