« SE3 2025/2026 EC3 » : différence entre les versions

De wiki-se.plil.fr
Aller à la navigation Aller à la recherche
Aucun résumé des modifications
Ligne 1 : Ligne 1 :
= Objectifs =
<br>
<span style="color:#800020;font-size: 1.2em; font-weight: bold;">Jour 1 : Moteur et Boutons</span>


Réaliser le programme "capteurs et actionneurs" du cours/TP concernant la programmation en C sur microcontrôleur AVR :
Pour cette première étape, j’ai choisi d’avancer progressivement. L’objectif était simplement de lire les boutons S1 et S2 afin de commander la rotation du servo-moteur dans les deux sens.


* contrôlez un servo-moteur en utilisant le minuteur 0 ;
<br>
* l’utilisateur utilise deux boutons pour changer l’angle ;
<span style="color:#800020;font-size: 1.2em; font-weight: bold;">Configuration des boutons</span>
* affichez l’angle de rotation sur les 7-segments :
**3 afficheurs doivent être utilisés ;
**utilisez la persistence rétinienne ;
**utilisez les interruptions générées par un second minuteur.


= Matériel nécessaire =
En consultant la datasheet ATmega328P,la section 13.2.1 (page 59),


* Arduino Uno avec câble USB ;
* Lorsqu’une broche est configurée en entrée DDR = 0 et que l’on écrit un 1 dans le registre PORT correspondant, la résistance pull-up interne est activée. Cela maintient la broche à 5V au repos.
* bouclier multi-fonctions ;
* servo-moteur.


= Historique =
on a donc :
* Bouton non appuyé : niveau  1
* Bouton appuyé : niveau 0


= Travail réalisé =
C’est pour cette raison que, dans la boucle principale, je teste un état logique bas 0 pour détecter un appui.
 
<br>
<span style="color:#800020;font-size: 1.2em; font-weight: bold;">Désactivation du buzzer du shield</span>
 
Au démarrage, le buzzer du shield émettait un signal sonore en continu.
Après vérification, celui-ci est piloté par un transistor PNP Q1. Pour le désactiver, il faut imposer un niveau logique haut (5V) sur la broche de commande. D’après la table 13-1 (page 60), cela se fait en configurant la broche en sortie DDR = 1 et en forçant le bit correspondant du registre PORT à 1.
J’ai donc configuré la broche PD3 en sortie et fixé son état à 1 dès l’initialisation du programme.
 
<br>
<span style="color:#800020;font-size: 1.2em; font-weight: bold;">Génération du signal PWM pour le servo</span>
 
Le servo-moteur nécessite un signal PWM d’environ 60 Hz.
 
J’ai utilisé le Timer 0 en mode Fast PWM (Mode 3), en activant les bits WGM01 et WGM00, conformément à la table 14-8 (page 86).
* Dans ce mode, le compteur évolue de 0 à 255.
* Pour que le signal soit transmis sur la broche PD6 (OC0A), j’ai activé le bit COM0A1 (table 14-3, page 84). ''"If one or both of the COM0A1:0 bits are set, the OC0A output overrides the normal port functionality of the I/O pin it is connected to."''
 
Étant donné que le microcontrôleur fonctionne à 16 MHz, j’ai appliqué un prescaler de 1024 via les bits CS02 et CS00 (page 87). Le calcul de la fréquence obtenue est le suivant :
 
<math>f = \frac{16\,000\,000}{1024 \times 256} \approx 61 \text{ Hz}</math>
 
La fréquence obtenue est donc d’environ 61 Hz, ce qui est compatible avec les exigences d’un servo-moteur standard.
 
<br>
<span style="color:#800020;font-size: 1.2em; font-weight: bold;">Justification des valeurs matérielles pour le servo Hitec HS-422</span>
 
Le moteur utilisé pour ce projet est un servo '''Hitec HS-422'''. En étudiant ses spécifications techniques officielles, le constructeur définit la plage de fonctionnement suivante :
* Le point central (neutre) est fixé à '''1500 µs (1,5 ms)'''.
* La plage d'impulsion garantie (Pulse Width) s'étend de '''900 µs (0,9 ms) à 2100 µs (2,1 ms)'''.
 
'''Calcul des valeurs du registre OCR0A :'''
Notre horloge Arduino fonctionne à 16 MHz. Avec un prescaler de 1024, la durée d'une seule incrémentation (un "tic") du Timer 0 est de :
<math>T_{tic} = \frac{1024}{16\,000\,000} = 0,000064 \text{ s} = 0,064 \text{ ms}</math>
 
Pour trouver la valeur technique à envoyer au registre, on divise le temps d'impulsion du constructeur par la durée de notre tic :
 
* '''Point central (1,5 ms) :'''
<math>\frac{1,5}{0,064} = 23,43</math>. En arrondissant, on obtient notre position de démarrage 23.
 
* '''Butée minimale constructeur (0,9 ms) :'''
<math>\frac{0,9}{0,064} = 14,06</math>. La limite officielle basse est de 14.
 
* '''Butée maximale constructeur (2,1 ms) :'''
<math>\frac{2,1}{0,064} = 32,81</math>. La limite officielle haute est de 33.
 
'' Dans le code final, les limites ont été étendues de 8 à 38, correspondant environ à 0,5 ms et 2,4 ms pour exploiter le maximum la mécanique du servo.''
 
<br>
<span style="color:#800020;font-size: 1.2em; font-weight: bold;">Code du Jour 1</span>


=== <span style="color:#800020;font-weight: bold;">1. Youssef_servo.c </span> ===
<syntaxhighlight lang="c" line="1">
<syntaxhighlight lang="c" line="1">
#ifndef F_CPU
#ifndef F_CPU
Ligne 27 : Ligne 72 :


#include <avr/io.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <util/delay.h>
#define LATCH PD4  // Signal de validation
#define CLOCK PD7  // Signal d'horloge
#define DATA  PB0  // Fil de données
volatile uint16_t angle = 90;
const uint8_t segments[] = {~0x3F,~0x06,~0x5B,~0x4F,~0x66,~0x6D,~0x7D,~0x07,~0x7F,~0x6F};
//Le symbole '~' inverse les bits car l'afficheur s'allume avec du 0V
void envoyer(uint8_t val) {
    for (uint8_t i = 0; i < 8; i++) {
        // On vérifie chaque bit de l'octet du plus grand au plus petit
        if (val & (1 << (7 - i))){
            PORTB |= (1 << DATA);  // On envoie un 1 (5V)
        } else {
            PORTB &= ~(1 << DATA); // On envoie un 0 (0V)
        }
       
        // On actionne l'horloge  manuellement pour faire avancer le bit
        PORTD |= (1 << CLOCK);
        PORTD &= ~(1 << CLOCK);
    }
}
ISR(TIMER2_COMPA_vect) {
    // static permet de garder en mémoire quel digit (0, 1 ou 2) on doit allumer
    static uint8_t digit = 0;
   
    // On extrait le bon chiffre du nombre total Centaines, Dizaines et Unités
    uint8_t val = (digit == 0) ? (angle/100) : (digit == 1) ? (angle/10)%10 : (angle%10);
    PORTD &= ~(1 << LATCH);      // On bloque l'affichage le temps de préparer les données
    envoyer(segments[val]);      // chiffre
    envoyer(1 << digit);          // digit
    PORTD |= (1 << LATCH);      // On déverrouille pour afficher le résultat final
    // On prépare le tour suivant
    digit = (digit + 1) % 3;
}


int main(void) {
int main(void) {
     // On définit les pins comme des sorties (DDR = 1)
     // On définit les pins comme des sorties (DDR = 1)
     DDRD |= (1<<LATCH) | (1<<CLOCK) | (1<<PD6) | (1<<PD3);
     DDRD |= (1<<PD6) | (1<<PD3);
    DDRB |= (1<<DATA);
      
      
     // On force le buzzer au silence
     // On force le buzzer au silence
     PORTD |= (1<<PD3);  
     PORTD |= (1<<PD3);  


     // On active les résistances internes
     // On active les résistances internes pour les boutons
     PORTC |= (1<<PC1) | (1<<PC2);
     PORTC |= (1<<PC1) | (1<<PC2);


Ligne 85 : Ligne 88 :
     TCCR0B = (1<<CS02) | (1<<CS00); // On ralentit le chrono pour avoir du 61Hz
     TCCR0B = (1<<CS02) | (1<<CS00); // On ralentit le chrono pour avoir du 61Hz
      
      
     // On règle une alerte toutes les 1 milliseconde pour changer de chiffre
     uint8_t pos = 23; // Valeur de démarrage (moteur à l'arrêt)
    TCCR2A = (1<<WGM21);           // Mode CTC
    TIMSK2 |= (1<<OCIE2A);        // On active l'alerte
    OCR2A = 249;                  // Cible
    TCCR2B = (1<<CS22);           
 
    sei(); // On autorise les alertes à fonctionner
   
    uint8_t pos = 23;
 


     while (1) {
     while (1) {
         // Lecture des boutons   
         // Lecture des boutons   
         if (!(PINC & (1<<PC1)) && pos > 8)  pos--; // Bouton S1 diminue
         if (!(PINC & (1<<PC1)) && pos > 8)  pos--; // Bouton S1 diminue
         if (!(PINC & (1<<PC2)) && pos < 38) pos++; // Bouton S2 augmente
         if (!(PINC & (1<<PC2)) && pos < 38) pos++; // Bouton S2 augmente
          
          
         OCR0A = pos; // On envoie l'ordre de vitesse au moteur
         OCR0A = pos; // On envoie l'ordre de vitesse au moteur
          
          
        // Calcul pour transformer la valeur technique en un chiffre lisible (0-180)
         _delay_ms(100); // Délai pour que le réglage du servo ne soit pas trop rapide
        angle = (uint32_t)(pos - 8) * 180 / 30;
       
         _delay_ms(100); // Délai pour le réglage du servo soit pas rapide
     }
     }
}
}
</syntaxhighlight>
</syntaxhighlight>
= Documents Rendus =

Version du 2 mars 2026 à 22:31


Jour 1 : Moteur et Boutons

Pour cette première étape, j’ai choisi d’avancer progressivement. L’objectif était simplement de lire les boutons S1 et S2 afin de commander la rotation du servo-moteur dans les deux sens.


Configuration des boutons

En consultant la datasheet ATmega328P,la section 13.2.1 (page 59),

  • Lorsqu’une broche est configurée en entrée DDR = 0 et que l’on écrit un 1 dans le registre PORT correspondant, la résistance pull-up interne est activée. Cela maintient la broche à 5V au repos.

on a donc :

  • Bouton non appuyé : niveau 1
  • Bouton appuyé : niveau 0

C’est pour cette raison que, dans la boucle principale, je teste un état logique bas 0 pour détecter un appui.


Désactivation du buzzer du shield

Au démarrage, le buzzer du shield émettait un signal sonore en continu. Après vérification, celui-ci est piloté par un transistor PNP Q1. Pour le désactiver, il faut imposer un niveau logique haut (5V) sur la broche de commande. D’après la table 13-1 (page 60), cela se fait en configurant la broche en sortie DDR = 1 et en forçant le bit correspondant du registre PORT à 1. J’ai donc configuré la broche PD3 en sortie et fixé son état à 1 dès l’initialisation du programme.


Génération du signal PWM pour le servo

Le servo-moteur nécessite un signal PWM d’environ 60 Hz.

J’ai utilisé le Timer 0 en mode Fast PWM (Mode 3), en activant les bits WGM01 et WGM00, conformément à la table 14-8 (page 86).

  • Dans ce mode, le compteur évolue de 0 à 255.
  • Pour que le signal soit transmis sur la broche PD6 (OC0A), j’ai activé le bit COM0A1 (table 14-3, page 84). "If one or both of the COM0A1:0 bits are set, the OC0A output overrides the normal port functionality of the I/O pin it is connected to."

Étant donné que le microcontrôleur fonctionne à 16 MHz, j’ai appliqué un prescaler de 1024 via les bits CS02 et CS00 (page 87). Le calcul de la fréquence obtenue est le suivant :

La fréquence obtenue est donc d’environ 61 Hz, ce qui est compatible avec les exigences d’un servo-moteur standard.


Justification des valeurs matérielles pour le servo Hitec HS-422

Le moteur utilisé pour ce projet est un servo Hitec HS-422. En étudiant ses spécifications techniques officielles, le constructeur définit la plage de fonctionnement suivante :

  • Le point central (neutre) est fixé à 1500 µs (1,5 ms).
  • La plage d'impulsion garantie (Pulse Width) s'étend de 900 µs (0,9 ms) à 2100 µs (2,1 ms).

Calcul des valeurs du registre OCR0A : Notre horloge Arduino fonctionne à 16 MHz. Avec un prescaler de 1024, la durée d'une seule incrémentation (un "tic") du Timer 0 est de :

Pour trouver la valeur technique à envoyer au registre, on divise le temps d'impulsion du constructeur par la durée de notre tic :

  • Point central (1,5 ms) :

. En arrondissant, on obtient notre position de démarrage 23.

  • Butée minimale constructeur (0,9 ms) :

. La limite officielle basse est de 14.

  • Butée maximale constructeur (2,1 ms) :

. La limite officielle haute est de 33.

Dans le code final, les limites ont été étendues de 8 à 38, correspondant environ à 0,5 ms et 2,4 ms pour exploiter le maximum la mécanique du servo.


Code du Jour 1

#ifndef F_CPU
#define F_CPU 16000000UL // On définit la vitesse de l'horloge 
#endif

#include <avr/io.h>
#include <util/delay.h>

int main(void) {
    // On définit les pins comme des sorties (DDR = 1)
    DDRD |= (1<<PD6) | (1<<PD3);
    
    // On force le buzzer au silence
    PORTD |= (1<<PD3); 

    // On active les résistances internes pour les boutons
    PORTC |= (1<<PC1) | (1<<PC2);

    // On crée un signal PWM rapide pour piloter le servo sur la pin D6
    TCCR0A = (1<<COM0A1) | (1<<WGM01) | (1<<WGM00); 
    TCCR0B = (1<<CS02) | (1<<CS00); // On ralentit le chrono pour avoir du 61Hz
    
    uint8_t pos = 23; // Valeur de démarrage (moteur à l'arrêt)

    while (1) {
        // Lecture des boutons  
        if (!(PINC & (1<<PC1)) && pos > 8)  pos--; // Bouton S1 diminue
        if (!(PINC & (1<<PC2)) && pos < 38) pos++; // Bouton S2 augmente
        
        OCR0A = pos; // On envoie l'ordre de vitesse au moteur
        
        _delay_ms(100); // Délai pour que le réglage du servo ne soit pas trop rapide
    }
}