Aller au contenu


Photo
- - - - -

Utilisation d'une librairie


  • Veuillez vous connecter pour répondre
12 réponses à ce sujet

#1 Astondb8-RX

Astondb8-RX

    Habitué

  • Membres
  • PipPip
  • 295 messages

Posté 17 mai 2011 - 02:30

Bonjour,

Je viens de lire (relire en fait mais bon...) l'excellent Tuto (de Philippe)sur la création de Librairies.
Tout cela est parfaitement clair et bien utile.

Oui mais voila, du coup, poussé par la curiosité, je me dit qu'il existe une librairie "Servo" qui pilote des servo moteurs.
Lorsque l'on appelle cette librairie, la ligne #include est ajoutée.
Bien je viens d'appeler la librairie Servo qui contiens tout le code qui vas piloter mon servomoteur. Jusque la cela semble simple.
Mais en fait pas tant que cela.
Le tuto "Morse" était pourtant clair, une LED sur la pin 13 on faisait dérouler le code et la Led clignotait comme précisé dans le code
dot, dot dot
dash, dash, dach
dot, dot, dot

Rien de plus simple.

Mais un servo moteur est un peu plus complexe à faire tourner...

Voilà le code de servo.h

#define Servo_h

#include

/*
* Defines for 16 bit timers used with Servo library
*
* If _useTimerX is defined then TimerX is a 16 bit timer on the curent board
* timer16_Sequence_t enumerates the sequence that the timers should be allocated
* _Nbr_16timers indicates how many 16 bit timers are available.
*
*/

// Say which 16 bit timers can be used and in what order
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
#define _useTimer5
#define _useTimer1
#define _useTimer3
#define _useTimer4
typedef enum { _timer5, _timer1, _timer3, _timer4, _Nbr_16timers } timer16_Sequence_t ;

#elif defined(__AVR_ATmega32U4__)
#define _useTimer3
#define _useTimer1
typedef enum { _timer3, _timer1, _Nbr_16timers } timer16_Sequence_t ;

#elif defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__)
#define _useTimer3
#define _useTimer1
typedef enum { _timer3, _timer1, _Nbr_16timers } timer16_Sequence_t ;

#elif defined(__AVR_ATmega128__) ||defined(__AVR_ATmega1281__)||defined(__AVR_ATmega2561__)
#define _useTimer3
#define _useTimer1
typedef enum { _timer3, _timer1, _Nbr_16timers } timer16_Sequence_t ;

#else // everything else
#define _useTimer1
typedef enum { _timer1, _Nbr_16timers } timer16_Sequence_t ;
#endif

#define Servo_VERSION 2 // software version of this library

#define MIN_PULSE_WIDTH 544 // the shortest pulse sent to a servo
#define MAX_PULSE_WIDTH 2400 // the longest pulse sent to a servo
#define DEFAULT_PULSE_WIDTH 1500 // default pulse width when servo is attached
#define REFRESH_INTERVAL 20000 // minumim time to refresh servos in microseconds

#define SERVOS_PER_TIMER 12 // the maximum number of servos controlled by one timer
#define MAX_SERVOS (_Nbr_16timers * SERVOS_PER_TIMER)

#define INVALID_SERVO 255 // flag indicating an invalid servo index

typedef struct {
uint8_t nbr :6 ; // a pin number from 0 to 63
uint8_t isActive :1 ; // true if this channel is enabled, pin not pulsed if false
} ServoPin_t ;

typedef struct {
ServoPin_t Pin;
unsigned int ticks;
} servo_t;

class Servo
{
public:
Servo();
uint8_t attach(int pin); // attach the given pin to the next free channel, sets pinMode, returns channel number or 0 if failure
uint8_t attach(int pin, int min, int max); // as above but also sets min and max values for writes.
void detach();
void write(int value); // if value is < 200 its treated as an angle, otherwise as pulse width in microseconds
void writeMicroseconds(int value); // Write pulse width in microseconds
int read(); // returns current pulse width as an angle between 0 and 180 degrees
int readMicroseconds(); // returns current pulse width in microseconds for this servo (was read_us() in first release)
bool attached(); // return true if this servo is attached, otherwise false
private:
uint8_t servoIndex; // index into the channel data for this servo
int8_t min; // minimum is this value times 4 added to MIN_PULSE_WIDTH
int8_t max; // maximum is this value times 4 added to MAX_PULSE_WIDTH
};

#endif[/code]

et voici le code de servo.cpp


#include

#include "Servo.h"

#define usToTicks(_us) (( clockCyclesPerMicrosecond()* _us) / 8) // converts microseconds to tick (assumes prescale of 8) // 12 Aug 2009
#define ticksToUs(_ticks) (( (unsigned)_ticks * 8)/ clockCyclesPerMicrosecond() ) // converts from ticks back to microseconds


#define TRIM_DURATION 2 // compensation ticks to trim adjust for digitalWrite delays // 12 August 2009

//#define NBR_TIMERS (MAX_SERVOS / SERVOS_PER_TIMER)

static servo_t servos[MAX_SERVOS]; // static array of servo structures
static volatile int8_t Channel[_Nbr_16timers ]; // counter for the servo being pulsed for each timer (or -1 if refresh interval)

uint8_t ServoCount = 0; // the total number of attached servos


// convenience macros
#define SERVO_INDEX_TO_TIMER(_servo_nbr) ((timer16_Sequence_t)(_servo_nbr / SERVOS_PER_TIMER)) // returns the timer controlling this servo
#define SERVO_INDEX_TO_CHANNEL(_servo_nbr) (_servo_nbr % SERVOS_PER_TIMER) // returns the index of the servo on this timer
#define SERVO_INDEX(_timer,_channel) ((_timer*SERVOS_PER_TIMER) + _channel) // macro to access servo index by timer and channel
#define SERVO(_timer,_channel) (servos[SERVO_INDEX(_timer,_channel)]) // macro to access servo class by timer and channel

#define SERVO_MIN() (MIN_PULSE_WIDTH - this->min * 4) // minimum value in uS for this servo
#define SERVO_MAX() (MAX_PULSE_WIDTH - this->max * 4) // maximum value in uS for this servo

/************ static functions common to all instances ***********************/

static inline void handle_interrupts(timer16_Sequence_t timer, volatile uint16_t *TCNTn, volatile uint16_t* OCRnA)
{
if( Channel[timer] < 0 )
*TCNTn = 0; // channel set to -1 indicated that refresh interval completed so reset the timer
else{
if( SERVO_INDEX(timer,Channel[timer]) < ServoCount && SERVO(timer,Channel[timer]).Pin.isActive == true )
digitalWrite( SERVO(timer,Channel[timer]).Pin.nbr,LOW); // pulse this channel low if activated
}

Channel[timer]++; // increment to the next channel
if( SERVO_INDEX(timer,Channel[timer]) < ServoCount && Channel[timer] < SERVOS_PER_TIMER) {
*OCRnA = *TCNTn + SERVO(timer,Channel[timer]).ticks;
if(SERVO(timer,Channel[timer]).Pin.isActive == true) // check if activated
digitalWrite( SERVO(timer,Channel[timer]).Pin.nbr,HIGH); // its an active channel so pulse it high
}
else {
// finished all channels so wait for the refresh period to expire before starting over
if( (unsigned)*TCNTn < (usToTicks(REFRESH_INTERVAL) + 4) ) // allow a few ticks to ensure the next OCR1A not missed
*OCRnA = (unsigned int)usToTicks(REFRESH_INTERVAL);
else
*OCRnA = *TCNTn + 4; // at least REFRESH_INTERVAL has elapsed
Channel[timer] = -1; // this will get incremented at the end of the refresh period to start again at the first channel
}
}

#ifndef WIRING // Wiring pre-defines signal handlers so don't define any if compiling for the Wiring platform
// Interrupt handlers for Arduino
#if defined(_useTimer1)
SIGNAL (TIMER1_COMPA_vect)
{
handle_interrupts(_timer1, &TCNT1, &OCR1A);
}
#endif

#if defined(_useTimer3)
SIGNAL (TIMER3_COMPA_vect)
{
handle_interrupts(_timer3, &TCNT3, &OCR3A);
}
#endif

#if defined(_useTimer4)
SIGNAL (TIMER4_COMPA_vect)
{
handle_interrupts(_timer4, &TCNT4, &OCR4A);
}
#endif

#if defined(_useTimer5)
SIGNAL (TIMER5_COMPA_vect)
{
handle_interrupts(_timer5, &TCNT5, &OCR5A);
}
#endif

#elif defined WIRING
// Interrupt handlers for Wiring
#if defined(_useTimer1)
void Timer1Service()
{
handle_interrupts(_timer1, &TCNT1, &OCR1A);
}
#endif
#if defined(_useTimer3)
void Timer3Service()
{
handle_interrupts(_timer3, &TCNT3, &OCR3A);
}
#endif
#endif


static void initISR(timer16_Sequence_t timer)
{
#if defined (_useTimer1)
if(timer == _timer1) {
TCCR1A = 0; // normal counting mode
TCCR1B = _BV(CS11); // set prescaler of 8
TCNT1 = 0; // clear the timer count
#if defined(__AVR_ATmega8__)|| defined(__AVR_ATmega128__)
TIFR |= _BV(OCF1A); // clear any pending interrupts;
TIMSK |= _BV(OCIE1A) ; // enable the output compare interrupt
#else
// here if not ATmega8 or ATmega128
TIFR1 |= _BV(OCF1A); // clear any pending interrupts;
TIMSK1 |= _BV(OCIE1A) ; // enable the output compare interrupt
#endif
#if defined(WIRING)
timerAttach(TIMER1OUTCOMPAREA_INT, Timer1Service);
#endif
}
#endif

#if defined (_useTimer3)
if(timer == _timer3) {
TCCR3A = 0; // normal counting mode
TCCR3B = _BV(CS31); // set prescaler of 8
TCNT3 = 0; // clear the timer count
#if defined(__AVR_ATmega128__)
TIFR |= _BV(OCF3A); // clear any pending interrupts;
ETIMSK |= _BV(OCIE3A); // enable the output compare interrupt
#else
TIFR3 = _BV(OCF3A); // clear any pending interrupts;
TIMSK3 = _BV(OCIE3A) ; // enable the output compare interrupt
#endif
#if defined(WIRING)
timerAttach(TIMER3OUTCOMPAREA_INT, Timer3Service); // for Wiring platform only
#endif
}
#endif

#if defined (_useTimer4)
if(timer == _timer4) {
TCCR4A = 0; // normal counting mode
TCCR4B = _BV(CS41); // set prescaler of 8
TCNT4 = 0; // clear the timer count
TIFR4 = _BV(OCF4A); // clear any pending interrupts;
TIMSK4 = _BV(OCIE4A) ; // enable the output compare interrupt
}
#endif

#if defined (_useTimer5)
if(timer == _timer5) {
TCCR5A = 0; // normal counting mode
TCCR5B = _BV(CS51); // set prescaler of 8
TCNT5 = 0; // clear the timer count
TIFR5 = _BV(OCF5A); // clear any pending interrupts;
TIMSK5 = _BV(OCIE5A) ; // enable the output compare interrupt
}
#endif
}

static void finISR(timer16_Sequence_t timer)
{
//disable use of the given timer
#if defined WIRING // Wiring
if(timer == _timer1) {
#if defined(__AVR_ATmega1281__)||defined(__AVR_ATmega2561__)
TIMSK1 &= ~_BV(OCIE1A) ; // disable timer 1 output compare interrupt
#else
TIMSK &= ~_BV(OCIE1A) ; // disable timer 1 output compare interrupt
#endif
timerDetach(TIMER1OUTCOMPAREA_INT);
}
else if(timer == _timer3) {
#if defined(__AVR_ATmega1281__)||defined(__AVR_ATmega2561__)
TIMSK3 &= ~_BV(OCIE3A); // disable the timer3 output compare A interrupt
#else
ETIMSK &= ~_BV(OCIE3A); // disable the timer3 output compare A interrupt
#endif
timerDetach(TIMER3OUTCOMPAREA_INT);
}
#else
//For arduino - in future: call here to a currently undefined function to reset the timer
#endif
}

static boolean isTimerActive(timer16_Sequence_t timer)
{
// returns true if any servo is active on this timer
for(uint8_t channel=0; channel < SERVOS_PER_TIMER; channel++) {
if(SERVO(timer,channel).Pin.isActive == true)
return true;
}
return false;
}


/****************** end of static functions ******************************/

Servo::Servo()
{
if( ServoCount < MAX_SERVOS) {
this->servoIndex = ServoCount++; // assign a servo index to this instance
servos[this->servoIndex].ticks = usToTicks(DEFAULT_PULSE_WIDTH); // store default values - 12 Aug 2009
}
else
this->servoIndex = INVALID_SERVO ; // too many servos
}

uint8_t Servo::attach(int pin)
{
return this->attach(pin, MIN_PULSE_WIDTH, MAX_PULSE_WIDTH);
}

uint8_t Servo::attach(int pin, int min, int max)
{
if(this->servoIndex < MAX_SERVOS ) {
pinMode( pin, OUTPUT) ; // set servo pin to output
servos[this->servoIndex].Pin.nbr = pin;
// todo min/max check: abs(min - MIN_PULSE_WIDTH) /4 < 128
this->min = (MIN_PULSE_WIDTH - min)/4; //resolution of min/max is 4 uS
this->max = (MAX_PULSE_WIDTH - max)/4;
// initialize the timer if it has not already been initialized
timer16_Sequence_t timer = SERVO_INDEX_TO_TIMER(servoIndex);
if(isTimerActive(timer) == false)
initISR(timer);
servos[this->servoIndex].Pin.isActive = true; // this must be set after the check for isTimerActive
}
return this->servoIndex ;
}

void Servo::detach()
{
servos[this->servoIndex].Pin.isActive = false;
timer16_Sequence_t timer = SERVO_INDEX_TO_TIMER(servoIndex);
if(isTimerActive(timer) == false) {
finISR(timer);
}
}

void Servo::write(int value)
{
if(value < MIN_PULSE_WIDTH)
{ // treat values less than 544 as angles in degrees (valid values in microseconds are handled as microseconds)
if(value < 0) value = 0;
if(value > 180) value = 180;
value = map(value, 0, 180, SERVO_MIN(), SERVO_MAX());
}
this->writeMicroseconds(value);
}

void Servo::writeMicroseconds(int value)
{
// calculate and store the values for the given channel
byte channel = this->servoIndex;
if( (channel >= 0) && (channel < MAX_SERVOS) ) // ensure channel is valid
{
if( value < SERVO_MIN() ) // ensure pulse width is valid
value = SERVO_MIN();
else if( value > SERVO_MAX() )
value = SERVO_MAX();

value = value - TRIM_DURATION;
value = usToTicks(value); // convert to ticks after compensating for interrupt overhead - 12 Aug 2009

uint8_t oldSREG = SREG;
cli();
servos[channel].ticks = value;
SREG = oldSREG;
}
}

int Servo::read() // return the value as degrees
{
return map( this->readMicroseconds()+1, SERVO_MIN(), SERVO_MAX(), 0, 180);
}

int Servo::readMicroseconds()
{
unsigned int pulsewidth;
if( this->servoIndex != INVALID_SERVO )
pulsewidth = ticksToUs(servos[this->servoIndex].ticks) + TRIM_DURATION ; // 12 aug 2009
else
pulsewidth = 0;

return pulsewidth;
}

bool Servo::attached()
{
return servos[this->servoIndex].Pin.isActive ;
}
[/code]

Il est simple de se rendre compte, que pour un débutant, c'est pas la même limonade...

Donc il y a toujours nos pin a configurer en Output
Mais les impulsions, les angles, les temps etc, se règlent ou ?

Une librairie est sertes réutilisable à souhait, mais elle doit être adaptée à l'utilisation que l'on en fait à chaque fois ?

Quelqu'un pourrait détailler les modification de code à faire ?

Merci d'avance
Cdlt
Yves

#2 Jbot

Jbot

    Membre passionné

  • Membres
  • PipPipPip
  • 801 messages
  • Gender:Male
  • Location:Paris
  • Interests:Robotics

Posté 17 mai 2011 - 02:45

Oula, alors deja les bibliotheques, en général il ne faut pas y toucher. C'est quelque chose de fonctionnel que tu peux utiliser, mais il ne faut pas trop y toucher, surtout les libs fournies par arduino.

Les libs arduinos fournissent en général un exemple ou deux avec, que tu peux trouver dans
File => Examples => Servo => sweep (par exemple)

Ca te montrera comment faire tourner un servomoteur.

Sinon sur le site d'arduino, les libs sont expliquées aussi :
http://www.arduino.cc/en/Reference/Servo

#3 Astondb8-RX

Astondb8-RX

    Habitué

  • Membres
  • PipPip
  • 295 messages

Posté 17 mai 2011 - 03:33

Bonjour Jbot et merci

Donc tu veux dire que

// by Michal Rinott

#include

Servo myservo; // create servo object to control a servo

int potpin = 0; // analog pin used to connect the potentiometer
int val; // variable to read the value from the analog pin

void setup()
{
myservo.attach(9); // attaches the servo on pin 9 to the servo object
}

void loop()
{
val = analogRead(potpin); // reads the value of the potentiometer (value between 0 and 1023)
val = map(val, 0, 1023, 0, 179); // scale it to use it with the servo (value between 0 and 180)
myservo.write(val); // sets the servo position according to the scaled value
delay(15); // waits for the servo to get there
} [/code]

est suffisant pour faire tourner mon servo... (je parle de mon servomoteur mon servo à moi comme tu peux le voir c'est moins simple à gerer)

Une pin pour un Pot
Mais elle est ou cette la pin moteur ?.

Dans le second exemple, je connecte mon servi Pin 9 (bon ca c'est modifiable bien sur)

// by BARRAGAN
// This example code is in the public domain.


#include

Servo myservo; // create servo object to control a servo
// a maximum of eight servo objects can be created

int pos = 0; // variable to store the servo position

void setup()
{
myservo.attach(9); // attaches the servo on pin 9 to the servo object
}


void loop()
{
for(pos = 0; pos < 180; pos += 1) // goes from 0 degrees to 180 degrees
{ // in steps of 1 degree
myservo.write(pos); // tell servo to go to position in variable 'pos'
delay(15); // waits 15ms for the servo to reach the position
}
for(pos = 180; pos>=1; pos-=1) // goes from 180 degrees to 0 degrees
{
myservo.write(pos); // tell servo to go to position in variable 'pos'
delay(15); // waits 15ms for the servo to reach the position
} [/code]

Cela veux dire,
que mon servo est alimenté directement via une alimentation externe
et que le 3eme fil (celui du milieu) est a connecter Pon9 pour la commande ?



#4 Astondb8-RX

Astondb8-RX

    Habitué

  • Membres
  • PipPip
  • 295 messages

Posté 17 mai 2011 - 03:35

Désole j'ai répondu avant de lire ton lien jusqu'au bout

C'est clairement expliqué

Cdlt
Yves

#5 Jbot

Jbot

    Membre passionné

  • Membres
  • PipPipPip
  • 801 messages
  • Gender:Male
  • Location:Paris
  • Interests:Robotics

Posté 17 mai 2011 - 03:51

Du coup, il reste encore une question non élucidée ou pas ? ^^

#6 Astondb8-RX

Astondb8-RX

    Habitué

  • Membres
  • PipPip
  • 295 messages

Posté 17 mai 2011 - 03:56

Je te dirais ca quand j'aurais compris pourquoi j'ai perdu le driver

Tout est a refaire
et Jean est aller tellement vite que je n'ai pas vu ce qu'il a fait

Mais bon je vais trouver

#7 Astondb8-RX

Astondb8-RX

    Habitué

  • Membres
  • PipPip
  • 295 messages

Posté 17 mai 2011 - 04:11

C'est bon, ca marche et mon servo aussi...

Magique cette librairie et d'une simplicité à mettre en ouvre malgré la complexité du code.


Merci encore Jo

Cdlt
Yves

#8 Jbot

Jbot

    Membre passionné

  • Membres
  • PipPipPip
  • 801 messages
  • Gender:Male
  • Location:Paris
  • Interests:Robotics

Posté 17 mai 2011 - 04:16

C'est le principe des librairies, faire une interface simple a utiliser meme si derriere il y a une usine a gaz.

#9 Astondb8-RX

Astondb8-RX

    Habitué

  • Membres
  • PipPip
  • 295 messages

Posté 17 mai 2011 - 04:26

Sauf que impossible de dépasser (se qui semble être) les 180°

Le peux donner des valeurs
de 1 a 180 à ma boucle aller (for(pos = 0; pos < 180; pos += 1))
et de 180 ) 1 sur ma boucle retour (for(pos = 360; pos>=1; pos-=1))

par contre, des que je dépasse 180°, le déplacement s'arrête à 180° quoi qu'il arrive.
Pourtant, in servo est capable de faire 360° et même en théorie, tourner en continu non ?

Cdlt
Yves

#10 Hexa Emails

Hexa Emails

    Habitué

  • Membres
  • PipPip
  • 439 messages
  • Gender:Male

Posté 17 mai 2011 - 04:56

heu...non. Un serov MODIFIE le peux. je te laisse chercher un peu ^^
la plupard des servos sont mécaniquement incapable de faire plus de 180° (butée, essaie lorsqu'il n'est pas alimenté)

#11 Astondb8-RX

Astondb8-RX

    Habitué

  • Membres
  • PipPip
  • 295 messages

Posté 17 mai 2011 - 05:19

Effectivement Alexy

Cela veux donc dire que passé cette limite de 180°; seul les PàP ont une précision suffisante pour reproduire une mouvement ou plus de 180° est nécessaire ?

De plus, en utilisant un système d'engrenage, il doit être possible de démultiplier ces 180° en une rotation plus longue (voir même plusieurs rotation non ?

Cdlt
Yves



#12 Astondb8-RX

Astondb8-RX

    Habitué

  • Membres
  • PipPip
  • 295 messages

Posté 17 mai 2011 - 05:50

Donc
Aucun problème avec l'exemple "Sketch"
Par contre rien avec Knob
Mais la je ne comprend pas le câblage...
La pin A0 sur un Pot (l'autre pin du pot a la masse bien sur
La pin Num 9 + du servo
L'autre fil du servo a la masse
Mais cela ne suffit pas, il faut bien que le pot soit relié au moteur par la carte ou en direct.
Cdlt
Yves

#13 Hexa Emails

Hexa Emails

    Habitué

  • Membres
  • PipPip
  • 439 messages
  • Gender:Male

Posté 17 mai 2011 - 07:05

Alors:

pour ton 1 er post: certains servos tournent de plusieurs tours, mais tu peux utiliser des PàP voir des CC si tu as un capteur de retour d'info (en fait ça revient à se fabriquer un servo). Tu peux aussi démultiplier hein, la robotique c'est aussi de la mécanique, et une mécanique bien pensée peut faire des miracles.

pour ton 2nd post: le potard sert de catpeur, une extrimité sur +5, l'autre à la masse. La broche du milieu de ton potar aura un potentiel compris entre 0 et 5V, le CAN (convertisseur analogique numérique) de l'arduino vient lire cette valeur et commande le servo en conséquence: 0V 0° et 5V 180° je suppose?




1 utilisateur(s) li(sen)t ce sujet

0 members, 1 guests, 0 anonymous users