Go Down

Topic: [RESOLU] ServoMoteurs + CTC (ou PWM) AnyPins (Read 1 time) previous topic - next topic

zoroastre

Jul 16, 2012, 10:50 pm Last Edit: Jul 17, 2012, 08:49 pm by zoroastre Reason: 1
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 ;)

The code :

Code: [Select]

#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.
Gné! ;)

mrbbp

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

MACMan8

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 !
Lao Tseu l'a dit "Longue est la route qui mène à la connaissance, et nombreux sont les péages".

mrbbp

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

zoroastre

#4
Jul 17, 2012, 08:29 pm Last Edit: Jul 17, 2012, 08:35 pm by zoroastre Reason: 1
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  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 :

Code: [Select]

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

Code: [Select]

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

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


@+

Zoroastre.
Gné! ;)

zoroastre

#5
Jul 17, 2012, 08:48 pm Last Edit: Jul 17, 2012, 11:14 pm by zoroastre Reason: 1
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: [Select]
#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.
Gné! ;)

Go Up