Carte Romeo

De Wiki Arobose
m (Commande à distance)
 
Ligne 21 : Ligne 21 :
  
 
==== Langage C ====
 
==== Langage C ====
 +
===== Fonctions PWM =====
 
Pour générer un signal PWM, le microcontroleur a besoin d'un timer interne. Il utilise le timer 3A  pour E1 et le timer 4D pour E2.
 
Pour générer un signal PWM, le microcontroleur a besoin d'un timer interne. Il utilise le timer 3A  pour E1 et le timer 4D pour E2.
  
 
Dans un premier temps, il faut initialiser les timers en mode PWM. Nous configurons le mode PWM avec des valeurs fréquemment utilisées pour la commande de moteurs.
 
Dans un premier temps, il faut initialiser les timers en mode PWM. Nous configurons le mode PWM avec des valeurs fréquemment utilisées pour la commande de moteurs.
<syntaxhighlight lang="php">
+
<syntaxhighlight lang="c">
 
/* timer3 is used for phase-correct 8-bits PWM mode  
 
/* timer3 is used for phase-correct 8-bits PWM mode  
 
and with clock prescaled to system clock divided by 64 */
 
and with clock prescaled to system clock divided by 64 */
Ligne 54 : Ligne 55 :
  
 
Il faut activer les pins utilisées par le L298 (5:OC3A et 6:OC4D).
 
Il faut activer les pins utilisées par le L298 (5:OC3A et 6:OC4D).
<syntaxhighlight lang="php">
+
<syntaxhighlight lang="c">
 
/* enable OC4D (pin D6) as pwm output */
 
/* enable OC4D (pin D6) as pwm output */
 
void
 
void
Ligne 75 : Ligne 76 :
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
 +
Pour finir, il faut indiquer la valeur du PWM : cette valeur indique le temps haut du signal compris entre 0 et 255. Par exemple, pour avoir un signal carré (temps haut = temps bas), il faut avoir une valeur de 127. Ces valeurs sont écrites dans les registres OCRnx.
 +
 +
<syntaxhighlight lang="c">
 +
 +
/* write value a in PWM 3A */
 +
void
 +
pwm_3A_write(char a)
 +
{
 +
  OCR3A = a;
 +
}
 +
 +
/* write value a in PWM 4D */
 +
void
 +
pwm_4D_write(char a)
 +
{
 +
  OCR4D = a;
 +
}
 +
 +
</syntaxhighlight>
 +
 +
===== Fonctions moteur =====
 
Nous avons ensuite créer des fonctions qui permettent au robot d'avancer, reculer et tourner en précisant la vitesse (de 0 à 255).
 
Nous avons ensuite créer des fonctions qui permettent au robot d'avancer, reculer et tourner en précisant la vitesse (de 0 à 255).
<syntaxhighlight lang="php">
+
<syntaxhighlight lang="c">
 +
 
 
/* stop */
 
/* stop */
 
void
 
void
 
stop(void)
 
stop(void)
 
{
 
{
 +
  PORTD &= ~(1 << PORT4); /* E1 (PD4) = 0 */
 +
  PORTE &= ~(1 << PORT6); /* E2 (PE6) = 0 */
 
   pwm_4D_write(0);
 
   pwm_4D_write(0);
 
   pwm_3A_write(0);
 
   pwm_3A_write(0);
Ligne 88 : Ligne 113 :
 
  * speed from 0 to 255 */
 
  * speed from 0 to 255 */
 
void
 
void
advance(char speed)
+
advance(uint8_t m1_speed, uint8_t m2_speed)
 
{
 
{
 
   PORTD |= (1 << PORT4); /* E1 (PD4) = 1 */
 
   PORTD |= (1 << PORT4); /* E1 (PD4) = 1 */
 
   PORTE |= (1 << PORT6); /* E2 (PE6) = 1 */
 
   PORTE |= (1 << PORT6); /* E2 (PE6) = 1 */
   pwm_4D_write(speed);
+
   pwm_3A_write(m1_speed);
   pwm_3A_write(speed);
+
   pwm_4D_write(m2_speed);
 
}
 
}
  
Ligne 99 : Ligne 124 :
 
  * speed from 0 to 255 */
 
  * speed from 0 to 255 */
 
void
 
void
back_off(char speed)
+
back_off(uint8_t m1_speed, uint8_t m2_speed)
 
{
 
{
 
   PORTD &= ~(1 << PORT4); /* E1 (PD4) = 0 */
 
   PORTD &= ~(1 << PORT4); /* E1 (PD4) = 0 */
 
   PORTE &= ~(1 << PORT6); /* E2 (PE6) = 0 */
 
   PORTE &= ~(1 << PORT6); /* E2 (PE6) = 0 */
   pwm_4D_write(speed);
+
   pwm_3A_write(m1_speed);
   pwm_3A_write(speed);
+
   pwm_4D_write(m2_speed);
 
}
 
}
  
Ligne 110 : Ligne 135 :
 
  * speed from 0 to 255 */
 
  * speed from 0 to 255 */
 
void
 
void
turn_left(char speed)
+
turn_left(uint8_t m1_speed, uint8_t m2_speed)
 
{
 
{
 
   PORTD &= ~(1 << PORT4); /* E1 (PD4) = 0 */
 
   PORTD &= ~(1 << PORT4); /* E1 (PD4) = 0 */
 
   PORTE |= (1 << PORT6); /* E2 (PE6) = 1 */
 
   PORTE |= (1 << PORT6); /* E2 (PE6) = 1 */
   pwm_4D_write(speed);
+
   pwm_3A_write(m1_speed);
   pwm_3A_write(speed);
+
   pwm_4D_write(m2_speed);
 +
 
 
}
 
}
  
Ligne 121 : Ligne 147 :
 
  * speed from 0 to 255 */
 
  * speed from 0 to 255 */
 
void
 
void
turn_right(char speed)
+
turn_right(uint8_t m1_speed, uint8_t m2_speed)
 
{
 
{
 
   PORTD |= (1 << PORT4); /* E1 (PD4) = 1 */
 
   PORTD |= (1 << PORT4); /* E1 (PD4) = 1 */
 
   PORTE &= ~(1 << PORT6); /* E2 (PE6) = 0 */
 
   PORTE &= ~(1 << PORT6); /* E2 (PE6) = 0 */
   pwm_4D_write(speed);
+
   pwm_3A_write(m1_speed);
   pwm_3A_write(speed);
+
   pwm_4D_write(m2_speed);
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
Pour finir, les fonctions main() et setup()
+
Pour finir, la fonction principale main() et les fonctions setup() qui définissent l'état des pins (entrée ou sortie) et active les modules PWM.
<syntaxhighlight lang="php">
+
<syntaxhighlight lang="c">
 
void
 
void
setup(void)
+
motor_setup(void)
 
{
 
{
   pin_setup();
+
   /* Motor controller */
 +
  DDRD = (1 << PORTD4); /* E1 (PD4) as output pin */
 +
  DDRE = (1 << PORTE6); /* E2 (PE6) as output pin */
 +
 
 
   pwm_3_setup();
 
   pwm_3_setup();
 
   pwm_4_setup();
 
   pwm_4_setup();
 +
 
   pwm_3A_enable();
 
   pwm_3A_enable();
 
   pwm_4D_enable();
 
   pwm_4D_enable();
 +
}
 +
 +
void
 +
setup(void)
 +
{
 +
  motor_setup();
 
}
 
}
  
Ligne 155 : Ligne 191 :
 
</syntaxhighlight>
 
</syntaxhighlight>
  
[[Categorie:Produits]]
+
== Communication XBee ==
 +
Les modules Xbee de digi sont des modules de communication sans fils respectant la norme 802.15.4. Ces modules sont très simples à utiliser car ils convertissent les signaux Xbee en commande série.
 +
 
 +
La carte ROMEO est conçue pour recevoir un module Xbee lui permettant de communiquer à distance via sa liaison série. Le module est branché sur les pins 0 (RXD1) et 1 (TXD1) du microcontroleur utilisant l'USART 1.
 +
 
 +
=== Commande à distance ===
 +
Nous allons utiliser ce module pour piloter le robot à distance à partir du PC en envoyant un caractère de commande à la carte ROMEO. Pour cela, il faut également un module et un adaptateur XBee USB pour le PC.
 +
 
 +
Dans un premier temps, nous allons configurer la liaison série. Le message envoyé contient 8 bits de données et 1 bit de stop sans contrôle de parité. La vitesse de transmission est de 9600 bauds.
 +
 
 +
<syntaxhighlight lang="c">
 +
/* setup USART1
 +
* speed : 9600 Baud
 +
* 8-bits, no parity, 1-stop-bit */
 +
void
 +
USART_1_setup(void)
 +
{
 +
  DDRD &= ~(1 << PD2); /* initialize pin PD2 (RX) as input pin */
 +
  DDRD |= (1 << PD3); /* initialize pin PD3 (TX) as output pin */
 +
 
 +
  UBRR1 = 103; /* 9600 Baud at 16MHz */
 +
 
 +
  UCSR1A = 0;
 +
 
 +
  /* 8-bits, no parity, 1-stop-bit */
 +
  UCSR1C = (1 << UCSZ11) | (1 << UCSZ10);
 +
}
 +
</syntaxhighlight>
 +
 
 +
Il existe 2 solutions pour recevoir une donnée via la liaison série :
 +
La première solution est très simple mais elle est bloquante. En effet, si aucune donnée n'est reçue (perte de communication), le programme va rester bloqué dans le « while » ce qui peut être gênant pour le système.
 +
 
 +
<syntaxhighlight lang="c">
 +
 
 +
void
 +
USART_1_receiver_enable(void)
 +
{
 +
  UCSR1B |= (1 << RXEN1);
 +
}
 +
 
 +
unsigned char
 +
USART_1_receive(void)
 +
{
 +
  // wait for data to be received
 +
  while (!(UCSR1A & (1 << RXC1)))
 +
    ;
 +
  // get and return received data from buffer
 +
  return UDR1 ;
 +
}
 +
</syntaxhighlight>
 +
 
 +
La seconde solution utilise les interruptions. La fonction ISR est exécutée seulement quand une donnée est reçue (flag levé). Le reste du temps, le programme est exécuté normalement sans attendre la réception d'une donnée.
 +
 
 +
<syntaxhighlight lang="c">
 +
volatile char receive_char;
 +
 
 +
void
 +
USART_1_receiver_interrupt_enable(void)
 +
{
 +
  UCSR1B |= (1 << RXEN1);
 +
  /* enable interrupt on the RXC1 flag */
 +
  UCSR1B |= (1 << RXCIE1);
 +
  SREG |= (1 << SREG_I);
 +
}
 +
 
 +
// receiver interrupt
 +
ISR( USART1_RX_vect)
 +
{
 +
  unsigned char dustbin = 0;
 +
 
 +
  receive_char = UDR1;
 +
}
 +
</syntaxhighlight>
 +
 
 +
La fonction principale va tester le caractère reçu : 'a' pour avancer, 'b' pour reculer, 'r' pour tourner a droite, 'l' pour tourner à gauche et n'importe quel autre caractère pour s’arrêter.
 +
 
 +
<syntaxhighlight lang="c">
 +
void
 +
pin_setup(void)
 +
{
 +
  /* Motor controller */
 +
  DDRD = (1 << PORTD4); /* PD4 as output pin */
 +
  DDRE = (1 << PORTE6); /* PE6 as output pin */
 +
}
 +
 
 +
void
 +
setup(void)
 +
{
 +
  pin_setup();
 +
 
 +
  pwm_3_setup();
 +
  pwm_4_setup();
 +
 
 +
  pwm_3A_enable();
 +
  pwm_4D_enable();
 +
 
 +
  USART_1_setup();
 +
  //USART_1_receiver_enable();
 +
  USART_1_receiver_interrupt_enable();
 +
}
 +
 
 +
int
 +
main(void)
 +
{
 +
  //char c;
 +
  setup();
 +
 
 +
  while (1)
 +
    {
 +
      //c = USART_1_receive();
 +
 
 +
      switch (/*c*/receive_char)
 +
        {
 +
      case 'a':
 +
        advance(100);
 +
        break;
 +
      case 'b':
 +
        back_off(100);
 +
        break;
 +
      case 'r':
 +
        turn_right(100);
 +
        break;
 +
      case 'l':
 +
        turn_left(100);
 +
        break;
 +
      default:
 +
        stop();
 +
        break;
 +
        }
 +
    }
 +
  return 0;
 +
}
 +
</syntaxhighlight>
 +
 
 +
== Conversion analogique / numérique ==
 +
 
 +
Les capteurs que nous utilisons retourne généralement une information (tension) sous forme analogique. Pour exploiter cette information, le microcontroleur doit la convertir en signal numérique.
 +
 
 +
Le convertisseur analogique / numérique de l'ATMEGA32U4 est un convertisseur 10 bits soit une précision de 1024 points. En se basant sur une tension de référence (généralement la tension d'alimentation), il va, par approximation successive, définir la valeur numérique correspondante à la tension.
 +
 
 +
Dans un premier temps, nous allons configurer le convertisseur. Le microcontroleur a 6 entrées analogiques sur le port F qu'il faut configurer en entrée. La tension de référence pour effectuer la conversion est la tension d'alimentation Vcc = 5V.
 +
<syntaxhighlight lang="c">
 +
/* Voltage reference : Vcc
 +
* Right adjust result
 +
* and with clock prescaled to system clock divided by 1024 */
 +
void
 +
adc_setup(void)
 +
{
 +
  /* initialize PORTF pins as input pin */
 +
  DDRF = 0x00;
 +
 
 +
  /* set ADC prescale factor to 1024 */
 +
  ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);
 +
 
 +
  /* Vcc is reference voltage */
 +
  ADMUX |= (1 << REFS0);
 +
 
 +
  /* Set ADC to Free-Running Mode */
 +
  ADCSRB = 0;
 +
 
 +
  /* adc enable */
 +
  ADCSRA |= (1 << ADEN);
 +
}
 +
</syntaxhighlight>
 +
 
 +
Ensuite, nous allons lire la valeur de chaque entrée en démarrant la conversion.
 +
<syntaxhighlight lang="c">
 +
void
 +
adc_start_conversion(uint8_t pin)
 +
{
 +
  //adc_enable(pin);
 +
  ADMUX = (1 << REFS0) | pin;
 +
  DIDR0 = (1 << pin);
 +
  ADCSRA |= (1 << ADSC); // adc start conversion
 +
}
 +
 
 +
uint16_t
 +
adc_read(uint8_t pin)
 +
{
 +
  uint8_t low;
 +
  adc_start_conversion(pin);
 +
  while (ADCSRA & (1 << ADSC))
 +
    ;                    // wait for result
 +
  low = ADCL;  // must read LSB first
 +
  return (ADCH << 8) | low;    // must read MSB only once!
 +
}
 +
</syntaxhighlight>
 +
 
 +
[[Category:Produits]]

Version actuelle en date du 29 juillet 2013 à 13:29

Outils personnels
Catégories