Archives de
Auteur : Fabien_Guiraud

Réparation HDMI PS4

Réparation HDMI PS4

Diagnostic de la panne

Bon ici pas besoin d’être Docteur House…
J’ai acheté sur le bon coin une console PS4 pour pièces, le vendeur m’ayant indiqué qu’elle ne fonctionne plus depuis que son enfant s’est pris le pied dans le câble HDMI, celui-ci a donc été violemment arraché (le câble hein ! pas l’enfant).
Diagnostic simple : la sortie HDMI (seule sortie vidéo de la PS4) ne fonctionne plus.

 

Ouverture de la PS4

Je ne vais pas indiquer ici comment démonter la PS4 puisque d’autres l’ont fait beaucoup mieux que moi.
Je vous conseille donc la Vidéo de David Inman.

 

 

 

Première surprise…

Visiblement, je ne suis pas le premier à tenter la réparation de ce connecteur HDMI…
Il y a des traces évidentes de surchauffe proche du connecteur.
Pire ! Certains CMS (Composant Montés en Surface) ont disparu.

La photo ci-dessous montre la zone un peu brûlée avec les deux composants CMS.

Origines du problème…

Ici, un bricoleur est passé avant moi et a finalement fait plus de mal que de bien. On apprend tous les jours de nos bricolages. Le connecteur étant également fortement fixé avec beaucoup de soudure de l’autre côté de la carte, il faut donc beaucoup de chaleur pour parvenir à le sortir.
Mais vous devez diriger cette chaleur pour ne pas qu’elle brûle le reste de vos composants autour.
Pour ce faire, il vous faudra :
– Choisir un décapeur thermique précis (le diamètre de la sortie d’air chaud doit être assez petit)
– Appliquer de la pâte thermique sur le connecteur (cette pâte à la propriété de très bien conduire la chaleur, elle vous permettra de chauffer la zone visée plus facilement)
– Protéger les circuits autour du connecteur avec du scotch (évidemment, du scotch robuste en température type scotch Kapton)
J’en conviens, tout le monde n’a pas ce matériel à portée, mais il est simplement indispensable.

Faut-il continuer ou prononcer l’heure du décès ?

Fort heureusement pour moi, la PS4 est très répandue et grâce à d’autres bricoleurs du web j‘ai pu trouver des photos du HDMI de la PS4 avec les composants (merci à A.Paul auteur de la photo) :

Sur les deux composants, celui du haut ressemble à un condensateur en boîtier CMS 0603. La valeur de ce condensateur n’est pas identifiable directement.
Enfin en dessous, on a un composant qui semble être une diode (on voit sur le boîtier une barre blanche qui représente la cathode). La encore, impossible d’identifier directement quel type de diode.

 

On constate, en mesurant sur le circuit, que les deux composants sont connectés à la pin 18 du HDMI d’un coté et à la masse de l’autre côté.
Hors, la pin 18 du HDMI est l’alimentation 5 V. Il y a donc de bonnes chances que la diode soit une protection contre les surtensions (Diode Zener) et que le condensateur soit un simple condensateur de découplage. En théorie, ces composants ne sont donc pas indispensables au fonctionnement de la sortie HDMI.

On continue 🙂

Un mot sur la diode Zener

Les diodes Zener, contrairement à une diode conventionnelle qui ne laisse passer le courant électrique que dans un seul sens (le sens direct), sont conçues de façon à laisser également passer le courant inverse. Mais ceci uniquement si la tension à ses bornes est plus élevée que le seuil de l’effet d’avalanche. C’est cet effet qui est exploité ici pour protéger les circuits des surtensions. Sur la PS4 il s’agit vraisemblablement d’une diode zener de 5.6V et de 100mW (c’est celle installée sur la PS3).
Ce qui veut dire que si la tension dépasse ce seuil, la Zener se met à conduire le courant en inverse et limite la tension à cette valeur de 5.6V, protégeant ainsi les autres circuits. Mais ces diodes sont données avec un puissance maximale à respecter (elle est simple à calculer, c’est la tension zener multipliée par le courant qui traverse la diode). Pour cette diode de 100mW et de 5.6V le courant inverse ne doit donc pas dépasser 17.8mA. Hors une source HDMI peut fournir jusqu’à 55mA. Il me semble donc que cette diode est « sous-dimensionnée » par Sony (il y a sûrement une raison à ça, mais je ne la connais pas).
Pour en revenir à mon circuit, j’ai mesuré cette diode et elle conduit dans les deux sens, elle est donc cassée (elle a pu casser en température en étant brûlée par mon prédécesseur ou cassée en surtension puisqu’elle est sous-dimensionnée).
Je dois donc la retirer puisqu’elle maintient la sortie HDMI en court-circuit.

Un mot sur le condensateur de découplage

Les condensateurs de découplages sont extrêmement présents sur toutes les cartes électroniques.
Alors, à quoi ils servent ?
Pour le vulgariser, ils servent de « mini-réservoir d’énergie ». Si vous placez un circuit intégré sur une carte, celui-ci est alimenté. Il consomme alors du courant. Sauf que cette consommation de courant n’est pas constante dans le temps. Suivant l’activité du circuit intégré, on alterne les appels de courant et les chutes de courant. Irrémédiablement, ces variations de courant entraînent des variations des tensions d’alimentation. Et ça, ce n’est vraiment pas bon !
Pourquoi c’est si grave ? Qui dit variation des tensions dit variation des champs électriques et interférences…
Les alimentations sont polluées d’interférences sur toute la carte.
Ce condensateur n’est donc pas indispensable au sens strict du terme, mais il est fortement conseillé…
Une valeur standard conseillée est de 100nF et ici, il suffit qu’il tienne une tension de 5.6 Volts.
En résumé, il agit comme un filtre, empêchant les hautes fréquences de passer, et s’opposant aux brusques changements de tension.

Connecteur HDMI

Ces connecteurs HDMI sont traversants à travers le PCB par 4 points qui nécessitent un paquet de soudure de l’autre côté de la carte (flèches noires sur l’image à gauche). Ces points servent mécaniquement à la fixation du connecteur à la carte afin que la fiche ne bouge pas lors des branchements. Mais ces 4 points sont aussi connectés électriquement au potentiel de masse. 
Les câbles HDMI transportent des signaux vidéo « rapides ».
C’est-à-dire à des fréquences hautes. Ce qui implique des problématiques de CEM (Compatibilité Électromagnétique). En d’autres termes, ces signaux sont susceptibles d’être perturbés par les radiations électromagnétiques de l’environnement. Pour pallier à ce problème, les câbles et connecteurs HDMI sont blindés. Ils sont entourés de métal pour former une cage de Faraday. Les ondes ayant beaucoup de mal à traverser le métal, les flux vidéo sont protégés (jusqu’à un certain niveau de radiations dit « seuil de susceptibilité »).
Sous la flèche rouge, on trouve les 18 broches pour les connections électriques du HDMI lui-même.

Changement du connecteur

Pour le changement du connecteur, on suit la recette :

Retirez le connecteur

–  protection au scotch kapton des circuits autour du connecteur
– appliquer pâte thermique sur le connecteur
– chauffer localement et retirer le connecteur avec un pince quand la soudure est fondue

Bien nettoyer les pastilles de connexions

avec un gros fer à souder et de la tresse à dessouder : appuyez avec votre fer sur la tresse à dessouder elle-même appuyée sur les pastilles. Par capillarité, la soudure est capturée dans la tresse.
Si la tresse reste « collée » aux pastilles, surtout ne tirez pas pour retirer la tresse, vous risquez d’arracher la pastille. Une seule chose à faire, chauffer avec votre fer jusqu’à ce que la soudure soit à nouveau liquide.

– idem pour le nettoyage des trous de fixations.

Placez le nouveau connecteur

– placez le nouveau connecteur sans le souder et vérifiez à la loupe l’alignement des pattes du connecteur avec les pastilles. S’il n’est pas correct, vous devez tordre légèrement les 4 pattes traversantes du connecteur HDMI dans le bon sens pour compenser le décalage.

– quand vous êtes satisfait de l’alignement, soudez les 4 broches traversantes en maintenant le connecteur plaqué contre la carte (autant que possible).

Soudez les pinouilles

étalez de la pâte thermique sur les broches à souder
– munissez-vous de votre fer à souder avec la pointe la plus fine possible
Pour chaque broche :
– avec votre fer à souder, faite un contact rapide sur votre fil d’étain (cela permet de mettre une toute petite quantité de soudure sur la pointe du fer)
à l’aide de la loupe, appuyez avec votre fer sur la broche pour la maintenir quelques secondes contre la pastille, celle-ci doit rester collée à la pastille

Eventuelles corrections

Si comme moi, vous avez arraché une pastille en sortant le connecteur, vous pouvez corriger avec un fil pour établir la connexion :

Les connexions sont indiquées sur photo de A.Paul :

Tests des branchements

Pour vérifier les connections, j’ai opté pour la solution suivante : sacrifier un câble HDMI.
Ayant trouvé un tout petit câble HDMI inutile, je m’en suis servi pour valider point par point chaque connexion.
Cette méthode m’a permis de vérifier la conductimétrie a travers l’intégralité du connecteur (donc aussi que chaque pastille et bien connectée à sa broche) mais aussi de facilement mesurer entre les fils qu’il n’y a pas de contacts entre les pins de l’HDMI.
Pour se faire, vous avez besoin de deux documents la photo de A.Paul des points de connexions sur la carte et la description des signaux sur le câble HDMI :

Voici la manipulation :

Sur la photo ci-dessus les nappes roses, bleues et dorées représentent les groupes A,B,C,D , chacune contenant 3 fils.

Remontage et test de la PS4 (sans condensateur)

L’heure du verdict a sonné et c’est bien passé, j’ai une image sur mon écran d’ordinateur.
Victoire ! Ah non pas tout à fait…
En l’état, la console de jeux et totalement jouable sur le petit écran. Mais en y regardant de plus près, lorsque des images noires sont produites par la PS4, certains pixels blancs apparaissent et produisent un effet de « bruit » à l’écran.
J’ai donc testé avec un autre téléviseur plus grand et cette fois-ci, la console de jeux devient impraticable, des bandes entières de l’écran deviennent blanches de façon aléatoire…
Ce comportement fait fortement penser à des interférences, on en revient donc à notre condensateur, dont le manque se fait finalement sentir…

Rajout du condensateur

J’ai testé de rajouter un condensateur de découplage de 100nF. Malheureusement, je n’en avais pas en boîtier correspondant. Je l’ai donc connecté avec des petits-fils sur l’empreinte de la diode :

Le câble HDMI

La qualité du câble HDMI est essentielle ! Je le répète, ces signaux sont sensibles aux interférences.
En choisissant un bon câble, celui-ci aura un bon blindage et de bon contact (très conducteur).
Je vous conseille la marque LCS Orion sur amazon : ils ont un très bon rapport qualité/prix.
Leurs câbles sont très rigides : preuve de l’épaisseur du blindage et du tressage des fils.

 

Bilan

Avec l’ajout du condensateur de découplage et du câble HDMI de qualité, la PS4 fonctionne très bien sûr le grand téléviseur.
En y regardant de très prêt sur le téléviseur, sur une image fixe, on peut tout de même apercevoir quelques pixels qui varient.
Ce léger défaut est très probablement dû au fil placé pour faire la connexion manquante sur le connecteur HDMI.
Les résistances de contact des soudures entraînent une différence d’impédance entre la connexion faite avec le fil et les autres connexions faites par les pistes du PCB.

Cela dit, la console et tout à fait fonctionnelle, et on ne voit pas le défaut à moins de coller sur nez sur l’écran.
Cette réparation est donc terminée, la patiente est sauvée.
*générique de fin Dr House*

 

Micro-contrôleur – 2/ Les périphériques

Micro-contrôleur – 2/ Les périphériques

 Convertisseur Analogique vers Numérique (A/D)

Ce dernier permet de donner une valeur numérique à une information de type analogique. Si l’alimentation de l’ADC est de 5V et qu’il s’agit d’un convertisseur 10 bits, alors un signal d’entrée variant de 0 à 5V sera converti, de façon linéaire, en une valeur numérique variant de 0 à ((2^10) -1) bits soit 1023. Attention bien sur, à vérifier la fréquence d’échantillonnage. En fonction des variations du signal d’entrée, il faudra venir lire plus ou moins rapidement la valeur de l’ADC.

Niveau débutant

Pour apprendre à utiliser une telle fonction, lançons nous dans un exemple : mesurer une température !
Objectif : lire une température de -40°C à 120°C
Matériel :

  • Arduino Mega2560 (arduino basé sur notre micro-contrôleur exemple : le ATMEGA2560) Afficher l'image d'origine
  • sonde PT1000, Un résistance qui varie en fonction de la température, elle nous servira ici de capteur (on suppose que la sonde est parfaitement linéaire, sa résistance est de 820Ω à -40°C et de 1460Ω à 120°C).

Le capteur présente donc une première conversion de la température en résistance :

temp_resistancePour utiliser toute la plage en tension de mon convertisseur ADC, je dois convertir cette plage de résistance en une plage en tension de 0 à 5V. Pour cela j’utilise un montage électronique suivant:

pt1000_arduino

En simplifié*:
V- = (R3/(R3+R1)*5V=2.5V
V+ = (R4/(R4+R2)*5V varie de 2.5V à 3.3V quand la sonde R4 (PT1000) varie de 820Ω à   1460Ω
La tension différentiel (V+ – V-) varie donc de 0 à 700mV. On utilise l’amplificateur d’instrumentation INA114 pour amplifier la différence (V+-V-) par 7.25 (soit R5=8kΩ)

*remarque montage électronique :
Il s’agit ici de faire un montage simple pour montrer que la conversion est  nécessaire.
Nous avons considéré des alimentions parfaites, mais en pratique les variations en température entrainent des variations des alimentations. Pour concevoir un design électronique plus sérieux, il faudrait :
-réaliser un montage ratio-métrique (c’est a dire prendre en compte les variations de l’alimentation 5V dans la mesure)
-protéger l’ADC d’éventuelles surtensions,
-rajouter un étage de filtrage (réduire les perturbations)…

simu_pt1000_arduino

La simulation LTSPICE ci dessus nous permet de confirmer que ce montage converti notre plage de résistance en une plage de tension de 0 à 5V.

Ce signal peut maintenant être envoyé sur la pin d’entrée Analogique du microcontrôleur !
La fonction arduino « AnalogRead » prend en entré le numéro de la pin à lire et elle renvoit la valeur numérique interprétée :

tension_numerique

Dernière étape : retrouver la valeur de la température !
Pour cela on peut utiliser la fonction arduino « map » elle permet de faire facillement le transfert linaire d’une unité à une autre :
Premier paramètre: valeur numérique lue sur l’entrée analogique
Ensuite les paramètres de la droite de conversion linéaire : x1, x2, y1, y2.
Dans notre cas : x1=0; x2= 1023; y1=-40°C;  y2=120°c
map(temperature_en_numerique , 0,1023,-40,120);
La fonction renvoie donc la valeur correspondante.

nume_temp

Au bilan, le code suivant permet d’effectuer la lecture de la température toutes les 500ms:

#define Sensor_pin=0;
int Temperature=0;

void setup() 
{
pinMode(Sensor_pin,INPUT);
}

void loop() 
{
  // put your main code here, to run repeatedly:
Temperature=map(analogRead(Sensor_pin),0,1023,-40,120);
delay(500);
}
Lecture_temperature

Niveau intermédiaire

Cette fonction « analogRead » fonctionne sur toute les cartes arduino sans même que vous n’ayez besoin d’une seule ligne de code en plus…

On peut se demander comment cela est possible alors même que les cartes Arduino embarquent des micro-contrôleur différents…

but-how

Et bien arduino vous cache des choses… Pour vous simplifier la vie, une partie du code est caché (mais pas trop quand même). La page présenté à l’ouverture d’arduino a une fonction « setup » toute prête pour vos initialisation et une fonction « loop » équivalente du habituel « main ». Sauf que derrière tout ça, d’autre code est chargé dans le micro-contrôleur.

Vous pouvez tout de même accéder aux fonctions arduino en fouillant un peu dans les dossiers arduino :
votre dossier arduino\hardware\arduino\avr\cores\arduino\wiring_analog.c
en ouvrant le fichier « wiring_analog.c », on trouve la définition de la fonction AnalogRead()

analof_read

On voit que des commandes types « #if define » sont utilisées.
Ces commandes en « # » (on évitera de prononcer « HTAG » on est pas sur twiter) sont appelées directives de préprocesseur : elles ne sont pas des commandes destinées au processeur mais bien au compilateur !
Elles sont très utiles pour gérer des configurations.
Ainsi, avec l’aide de ces directives, la fonction trouve toute seule les ressources hardware du micro-contrôleur qui correspondent à la carte Arduino que vous avez choisie. Il vous suffit de sélectionner sous Arduino « Outil/Type de carte ».

Pour aller plus loin

La datasheet nous fournit un chapitre pour chaque périphérique et nous indique comment configurer le périphérique via ses registres.La photo ci dessus montre le schémas bloc du périphérique ADC de l’ATMEGA2560.

adc_arduino
On retrouve bien en haut du schéma le DATABUS 8Bits (Direct Memory access) pour s’interfacer avec le micro-processeur.
Ce dernier contrôle 4 registres sur l’ADC le  ADCMUX , le ADCSRB le ADCSRA et le ADCH/ADCL.

admux_aduino

Les bitsREFS1 et REFS2 pilotent un multiplexer qui choisit l’alimentation de l’ADC.

vref_adc_aduino

Le Bit ADLAR permet de présenter le résultat de la conversion ajusté à droite ou à gauche (comme le résultat de la conversion fait 10bits et est stocké dans un registre de 16bits, la data est « décallé » à gauche ou à droite). On laisse la valeur par défaut. Les bits MUX0 à MUX4 permettent, via un multiplexer, de sélectionner la pin à convertir parmi  les 15 entrées analogiques. Le micro-contrôleur est ainsi capable de numériser 15 informations directement. Dans le cas on l’on a plusieurs entrées analogiques à lire, il faudra d’autant plus surveiller les temps de conversions).

adcsrb_arduino

Le bit MUX5 va avec les autres bit MUX pour définir la pin d’entrée choisie mais aussi un gain suivant la table 24-4 de la datasheet de l’ATMEGA2560.
Les bits ADTS permettent de sélectionner la source du déclenchement en mode déclenchement automatique.

adcsra_arduino

Le bit ADEN doit être mis à 1 pour activer l’ADC.
Le bit ADSC doit être mis à 1  pour commencer la conversion (Start Conversion), il retourne tout seul à 0 à la fin de la conversion.
Le bit ADATE (Auto Trigger Enabled) permet de déclencher automatiquement une conversion (source du déclenchement défini dans ADCSRB par ADTS)
Le bit ADIF (Interrupt Flag) est mis à 1 quand la conversion est terminée et que la donnée est disponible dans le registre de sortie (ADCH/ADCL). Cette fonction n’est activée que si le bit ADIE  (Interrup Enable) est activé ainsi que le bit I de SREG. Le bit ADIF repasse à 0 automatique à l’exécution de l’interruption.
Le bit ADIE (Interupt Enable) mentionné ci dessus.
Le bits ADPS permettent de choisir la valeur du prescaler (c’est a dire la fréquence de fonctionnement du périphérique ADC). Ces bits conditionnent directement le temps de conversion.On considère 13 coups d’horloge pour faire une conversion analogique/numérique (25 pour la première conversion).
Avec un prescaler de 128, la fréquence du CPU est divisé par 128 soit 16MHz/128 =125kHz soit 8us le coup d’horloge donc 13*8 =104us pour faire une conversion.
Alors on pourrait se dire  : avec un prescaler de 4, la fréquence du CPU est divisé par 4 soit 16MHz/4=4MHz soit 25ns le coup d’horloge donc 13*25 =325ns pour faire une conversion.Mais ça ne fonctionnera pas puisque la datasheet nous dit aussi que pour une résolution de 10 bit, on ne peut pas excéder les 200kHz…
Si l’on regarde nos valeurs de prescaler et notre 16MHz de freq CPU on obtient les possibilités suivantes:

  • 16 MHz / 2 = 8 MHz
  • 16 MHz / 4 = 4 MHz
  • 16 MHz / 8 = 2 MHz
  • 16 MHz / 16 = 1 MHz
  • 16 MHz / 32 = 500 kHz
  • 16 MHz / 64 = 250 kHz
  • 16 MHz / 128 = 125 kHz

Donc pour 10 bit de résolution, la seule possibilité est le prescaler à 128 et on obtient 125kH soit 125kH divisé par 13 coups d’horloge 96 mesures par milliseconde.
Si l’on a  la possibilité de changer la fréquence du CPU à 12MHz avec le prescaler 64 on obtient 187kHz divisé par 13 coups d’horloge 143 mesures par milliseconde.
On est plus rapide sur la conversion de l’ADC mais on en perd sur le reste du code puisque le CPU tourne plus lentement…Le constructeur a cependant rédigé une application note indiquant que des fréquences inférieures au Méga Hertz ne diminue pas « de façon significative » la résolution de l’ADC.
On le voit donc, il s’agit d’un compromis entre rapidité et précision.
On pourra choisir un prescaleur entre 16 et 128 pour une fréquence entre 125kH et 1MHz donc entre 93 et 769 mesures par milliseconde.Dans le cas ou la mesure est lente (prescaler fort), il sera plus judicieux d’utiliser l’interruption sur le bit ADIF. Le processeur peut ainsi, lancer l’ordre de conversion, exécuter d’autres commandes pendant la durée de conversion, il est ensuite alerté par le BIT ADIF que la data est disponible, il vient alors la lire et retourne au reste du code.
Dans l’exemple de code donnée avec le analogRead de la température on estime que l’instruction analogRead prend 100us  (elle est « bloquante » pendant les 100us, puisque le pocesseur « attend » la fin de la conversion).

On peut donc passer de analogRead avec ses 10 mesures par milliseconde à une configuration jusqu’à 769 mesures par milliseconde.

Ce qu’il faut retenir : tout dépend de vos besoins, il est évident que pour mesurer l’évolution d’une température, la gestion du temps processeur n’est pas critique et la fonction analogRead toute prête convient très bien. En revanche, si vous voulez surveiller des signaux rapides, il va falloir étudier le fonctionnement du périphérique dans la datasheet du constructeur pour optimiser le temps de conversion et l’activité du processeur.

Les Timers/Compteurs

Niveau débutant

Les timers/counter sont des éléments essentiel dans la gestion d’une application embarquée.

Pour comprendre le rôle d’un timer, lançons nous dans une analogie !

Pour faire une recette de cuisine, vous exécutez les instructions de la recette, de la même façon, le processeur exécute les lignes de code. Dans votre recette on vous demande de mettre le gâteaux au four et de le laisser la 40 minutes. Dans ces cas la, nous humain, nous ne restons pas « plantés » devant le four à ne rien faire pendant 40 minutes… Nous vaquons à nos occupations en attendant que le minuteur du four sonne. Et bien c’est pareil pour le processeur, vous devez « gérer son planning » si vous ne voulez pas qu’il se retrouve à attendre bêtement sans rien faire d’autre.
Vous allez donc demander à votre processeur de lancer un minuteur et de faire le reste de ses occupations en attendent le signal de fin du minuteur. Ici Il faudra donc commander le périphérique de Timer Counter.

 

Niveau intermédiaire

Nous allons voir une application simple d’un timer.
On souhaite réaliser un programme qui fait clignoter une LED toute les 500 millisecondes et qui, en même temps, fait tourner un servo-moteur.

Voici un code très basique pour faire clignoté une LED :

void setup() 
{
  // initialize digital pin LED_BUILTIN as an output.
pinMode(LED_BUILTIN, OUTPUT);
}

// the loop function runs over and over again forever
void loop() 
{
 digitalWrite(LED_BUILTIN, HIGH);   // turn the LED on (HIGH is the voltage level)
 delay(1000);                       // wait for a second

 digitalWrite(LED_BUILTIN, LOW);    // turn the LED off by making the voltage LOW
delay(1000);                       // wait for a second
}
LED_Blink

 

Aucun soucis, la led va clinoter toute les 500ms. Maintenant rajoutons l’action de déplacement du servo moteur : (on utilise la library Servo.h et on affecte le pilotage du servo-moteur à la pin 9)

Sans Timer :

#include <Servo.h>
Servo myservo;  // create servo object to control a servo
// twelve servo objects can be created on most boards
int pos = 0;    // variable to store the servo position
void setup() {
  myservo.attach(9);  // attaches the servo on pin 9 to the servo object
   pinMode(LED_BUILTIN, OUTPUT);
}
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 >= 0; 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
  }
  digitalWrite(LED_BUILTIN, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(1000);                       // wait for a second
  digitalWrite(LED_BUILTIN, LOW);    // turn the LED off by making the voltage LOW
  delay(1000);                       // wait for a second
}

Résultat de ce programme :
La led s’allume pendant 500ms (le servomoteur est immobile).
La led s’éteint pendant 500ms  (le servomoteur est immobile).
Le servomoteur bouge de 0° à 180 ° (la LED reste éteinte).
Le servomoteur bouge de 180° à 0° (la LED reste éteinte).
Le programme ne fait pas les deux « en même temps » il attend dans la fonction « delays » sans rien faire d’autre.

C’est la q’intervient le Timer/counter ! Nous allons le programmer pour nous dire de faire une action toute les 500ms.
Comme tous les périphériques, le Timer/counter sera configuré via ses registres (présentés dans la datasheet du micro-contrôleur).

Pour aller plus loin

Implémentation du Timer :

  • Etape 1 : Choisir un le Timer/Counter

Les termes timer et counter sont associés parce que ces instruments permettent de compter le temps.
En fait, par « compter le temps » on veut dire compter les impulsions de l’horloge du microcontrôleur.

Prenons l’exemple de notre ATMEGA2560 cadencé à 16MHz : cela signifie que le signal généré par le quartz sur la carte électronique possède une fréquence de 16MHz soit une pulsation toute les 62,5 nanosecondes….
Si je veux compter un temps de 1 milliseconde je devrait compter (1 ms/ 62.5ns) soit 16000 « tick ». Or les timers possèdent une limitation. On peut par exemple trouver des timers 8bits ou 10bits capables de compter respectivement jusqu’à 255 et 1023 (donc insuffisant pour compter 16000 « tick »).
C’est pourquoi on a recours à un « prescaler ». Le « prescaler » permet justement de diviser l’horloge d’entrée d’un facteur choisi (en général 1,2,4,8 ou 16).
Reprenons notre comptage de 1 milliseconde, cette fois ci j’applique un prescaler de 16. Ma fréquence d’entré dans mon compteur est donc de 16MHz/16 soit
1 MHz, soit une période de 1 microseconde. J’ai donc besoin d’en compter 1000 pour atteindre une milliseconde. C’est possible avec un timer 10bits (qui va jusqu’à 1023).

Nous avons vu que pour utiliser un Timer il faut veiller à choisir un des Timer/Counter qui convient avec le bon prescaler (suivant le besoin de comptage et l’horloge de votre cible).

Il y a un autre élément à prendre en compte : la gestion des ressources.
Lorsqu’on utilise des librairies on gagne énormément de temps mais il faut aussi regarder quelles sont les ressources matérielles utilisées par ces librairies. Dans notre exemple nous utilisons la Library « Servo.h » qui nous permet facilement de piloter des servo-moteurs.
Il se trouve que cette librarie utilise des timer/counter pour générer les signaux de commande des servo-moteur. Si nous utilisons le même Timer que celui de la librairie, le code ne pourra pas fonctionner comme nous le voulons. En fait le code que l’on écrit nous même et le code de la Library risquent de configurer à différents moments le même timer ce qui entraînerai  un comportement aléatoire.
Maintenant que les contraintes sont identifiées, choisissons un Timer : le ATMEGA2560 nous propose en tout 5 périphériques de Timer/Counter numérotés de 0 à 5.

Le Timer/Counter 0 est un timer 8 bits donc pas intéressant pour nous, et en plus, il est utilisé pour les fonctions arduino  :__delay()__, __millis()__ et__micros()__.
Le Timer/Counter 5 est utilisé par la Library « Servo.h ».
Le Timer/Counter 2 est un timer 8 bits un peu particulier avec des fonctions supplémentaires.

 On peut choisir les Timer/Counter 1,3 ou 4 qui sont 16bits. Allons y pour le Timer/Counter 1  !

  • Etape 2 : Configuration du Timer Counter

Prenons donc la documentation technique de notre Timer/Counter1 sur ATMEGA2560.

Le constructeur nous propose la vu suivante du périphérique TIMER1.
Nous, utilisateur, avons accès aux registres via le databus à gauche du schéma. Ces registres sont les suivants : TCNT1, OCR1A, OCR1B, OCR1C,TCCR1A, TCCR1B,TCCR1C.
En sortie du périphérique Timer on trouve 3 signaux OC1A, OC1B, OC1C.
Dans notre cas, nous n’avons besoin que d’un signal qui changera d’état toute les millisecondes :OC1A.

Passons à la configuration :

A) Le Prescaler  

Il faut assigner la valeur correspondante aux bits CS12;CS11,CS10.

Ce qui donne : 

TCCR1B |=(1<<CS11)|(1<<CS10); //Prescaler 64
Prescaler_timer_arduino

Si cette écriture condensé ne vous parle pas voici une explication détaillée étape par étape :

B) Le mode de fonctionnement

Les Timers possèdent deux principaux modes :

  • Mode PWM pour (Pulse With Modulation) dans ce mode-là, on peut utiliser les timers pour générer des signaux PWM sans monopoliser le processeur. Les PWM sont très utilisés en électronique. Par exemple la Library « Servo.h » utilise ce mode la sur le Timer5 afin de contrôler un servomoteur. Ce n’est pas le mode qui nous intéresse ici.
  • Mode CTC pour « Clear Timer on Compare match ». On vient remettre à 0 le compteur du timer quand celui-ci a atteint la valeur spécifiée

Il faut assigner la valeur correspondante aux bits CS12;CS11,CS10.

Ce qui donne :

TCCR1B |= (1<<WGM12); //Mode CTC Clear Timer on Compare Match
Timer_Mode_Arduino
C) Registre de comparaison

A ce stade, nous avons configuré notre Timer avec une horloge de 16MHz divisée par un prescaler de 64 ce qui nous donne 250kHz. Nous devons donc compter 250 pulsations pour atteindre 1 milliseconde. On charge la valeur 250 dans le registre OCR1A.

OCR1A=250; //Comparator Value
Timer_Compar_Arduino
D) Activer l’interruption sur le Timer

Maintenant que nous avons réglé notre minuteur, la sortie OC1A va changer d’état chaque milliseconde. Il nous faut maintenant s’assurer que l’événement sera géré par le processeur.
Pour ce faire, nous allons associer une interruption à la sortie OC1A du Timer. C’est à dire que, quoi que fasse le processeur, si OC1A change d’état, le processeur met pause son activité et va immédiatement exécuter la fonction de gestion de cette interruption (« interrupt Handler ») avant de revenir à son activité initiale.
On configure le registre de gestion des interruptions du timer1 :

Ce qui donne :

TIMSK1 |= (1<<OCIEA);//Enable Timer Interrupt
Timer_Interrupt_Arduino
E) Bilan config Timer

Voici donc le code de configuration du timer, on veillera à désactiver les interruptions avant de commencer la configuration et à les réactiver ensuite. Cela nous permet de ne pas déclencher d’interruptions de façon intempestive.

  /*CONFIG TIMER*/
  noInterrupts();//First disable interrupt to avoid unpredicted interrupt trig
  TCCR1A=0;
  TCCR1B=0;
  TCNT1=0;
  TCCR1B|=(1<<WGM12); // MODE CTC Clear Timer on Compare match
  TCCR1B|=(1<<CS11)|(1<<CS10);//Pre-Scaler at 64 so 16MHz/64=250KHz
  OCR1A=250; // Set compare value to 250 ( on millisecond timer duration)
  TIMSK1|=(1<<OCIE1A);// Enable Interrupt on compare match (OC1A output)
  interrupts(); //Global interrupt enable
Arduino_Timer_config

Programme final : nous allons nous servir de cette interruption pour décharger le processeur de ces « delay() ».

#include <Servo.h>
#include <avr/sleep.h>
#define PIN_LED 13
#define PIN_SERVO 2
Servo myservo;  // create servo object to control a servo
int pos = 0;    // variable to store the servo position
int CPT_LED_ms=0;  //Millisecond counter
int CPT_servo_ms=0;//Millisecond counter
bool sens=true;
void setup() 
{
  /*INOPUT/OUTPUT Configuration */
  myservo.attach(PIN_SERVO);  // attaches the servo on pin 9 to the servo object
  pinMode(LED_BUILTIN, OUTPUT);
  
  /*CONFIG TIMER*/
  noInterrupts();//First disable interrupt to avoid unpredicted interrupt trig
  TCCR1A=0;
  TCCR1B=0;
  TCNT1=0;
  TCCR1B|=(1<<WGM12); // MODE CTC Clear Timer on Compare match
  TCCR1B|=(1<<CS11)|(1<<CS10);//Pre-Scaler at 64 so 16MHz/64=250KHz
  OCR1A=250; // Set compare value to 250 ( on millisecond timer duration)
  TIMSK1|=(1<<OCIE1A);// Enable Interrupt on compare match (OC1A output)
  interrupts(); //Global interrupt enable
}
//Timer 1 Interrupt Handler
ISR(TIMER1_COMPA_vect) //select the TIMER1_COMPA_vect interrupt from the interrupt manager
{
CPT_LED_ms++;
CPT_servo_ms++;
  //GESTION LED
  if(CPT_LED_ms>=500)
  {
  digitalWrite(PIN_LED,!digitalRead(PIN_LED));
  CPT_LED_ms=0;//reset millisecond counter
  }
  
  //GESTION Servo
  if(CPT_servo_ms>=15)
  {
    if(sens==true)//sens is true
    {
      if(pos<=176) // position is under 176
      {
       pos++;
       myservo.write(pos);
      }
      else //pos is above180
      {
       sens=false; // change sens
      }
    }
    else //sens is false
    {
      if(pos>0) // position is above 0
      {
       pos--;
       myservo.write(pos);
      }
      else //pos is 0
      {
       sens=true; //change sens
      }
    } 
  CPT_servo_ms=0;//reset millisecond counter  
  }
 }
void loop() 
{
set_sleep_mode(SLEEP_MODE_IDLE);
sleep_mode();// This fonction set the sleep enable to 1, then sleep the CPU and set back the sleep enable to 0 (before sleeping). This means the CPU is sleeping but the sleep enable is 0, so next interrupt event will "wake up" the CPU
}
Arduino_Timer_LED_SERVO

Remarque : si vous avez une erreur à la compilation due à une double utilisation d’un timer, c’est peut être que la Library Servo.h utilise également un des timers que vous cherchez à configurer. Pour contrer ce problème, il vous faut ouvrir le fichier ServoTimers.h sous C:\Program Files (x86)\Arduino\libraries\Servo\src\avr  et commenter la ligne correspondante. Par exemple, si vous ne voulez pas que Servo.h utilise le Timer 1 vous devez commenter la ligne « #define Timer1 ».

Ici aucun problème, la LED clignote comme il faut et le servomoteur tourne en continue en même temps (sourire)

Mais ce n’est pas le seul avantage de ce code.
Pour rappel le CPU reçoit une horloge de 16 millions de « tick » par secondes, hors dans notre cas, on lui demande seulement d’exécuter une action toutes les 1milliéme de seconde.Il travaille donc 0.006% du temps (un peu plus en réalité mais c’est de cet ordre là).
Rapportons ça à « l’échelle de temps humaine », si on vous demande de travailler 1 jour sur 16 000 jours, autant se reposer pendant 15 999 jours…
La boucle infinie « loop » étant déchargée de toute activités, on peut demander au microcontrôleur de se mettre en veille (comme un PC !) et celui-ci sera automatiquement réveillé par l’interruption pour exécuter ses taches périodiques.

Ceci nous permet de :
-baisser grandement la consommation de courant du microcontrôleur
-réduire  la température du microcontrôleur (puisqu’il consomme moins)
-augmenter la durée de vie du microcontrôleur (puisqu’il chauffe moins)

Les USART (Universal Synchronus/Asynchronus Receiver Transmiter)

Niveau débutant

En électronique, on diffère deux types de communications, série et parallèle.
Avec une communication parallèle, chaque fil représente un bit. Alors que sur une communication série, on envoie les bits « les un à la suite des autres » sur un seul fil.
Dans la communication série, l’horloge permet de connaître la cadence à laquelle les bits s’enchaînent (et donc de les lire correctement).
Cette horloge peut être transmise par un fil (communication série synchrone).
Ou alors elle n’est pas transmise mais les émetteurs et récepteurs sont préalablement réglés sur la même cadence, un bit de START et de STOP permettent de synchroniser la lecture des bits (communication série asynchrone).

Les USART (Universal Synchronus/Asynchronous Receiver Transmiter) sont les périphériques utilisés pour les communication série. Ils permettent de décharger le processeur de ses taches de communication.
Le processeur n’a pas besoin de venir écrire chaque bit sur la ligne le communication).
Le périphérique communiquera avec le processeur uniquement pour lui dire que des données ont été reçues et sont disponibles en lecture ou qu’il a terminé d’envoyer son message…

Pour aller plus loin

On commence à avoir l’habitude, nous allons attaquer le schémas bloc de l’USART du ATMEGA2560.

On distingue bien 3 parties :
-Génération de la clock (communication assynchrone)
-Transmitter (contrôle la pin TX)
-Receiver (contrôle la pin RX)

Micro-contrôleur – 1/ Architecture

Micro-contrôleur – 1/ Architecture

Introduction

Pour le vulgariser, le microcontrôleur est un « mini ordinateur » ; il est à l’échelle d’une carte électronique grâce à sa forte intégration (micro-électronique).

Exemple de micro-contrôleur que nous suivront dans ce tutoriel : le AVR le ATMEGA2560 :

Le voici en boîtier « 100A »  16mm² :

atmega2560-8au-500x500

atmega2560_pinout

 

 

 

 

 

 

Le voici en boitier « 100C1 » 7.2mm²

100c_package

 

atmega2560_pinout_2

Comme un ordinateur, il est donc principalement composé de :

  • A) Micro-processeur (unité de calcul)
  • B) Différent types de mémoires
  • C) Périphériques d’entrée sortie

Le Processeur

Autant commencer par le plus difficile : le processeur.

Niveau débutant

Le processeur exécute les « instructions machine ».
Les « instructions machines » c’est votre code ! Les outils de développement moderne nous permettent de programmer des instructions en langage haut niveau comme le C/C++ par exemple.
Ces outils de développement sont les logiciels de programmation fournis par les fabricants, ils auront alors pour charge de convertir du code en C/C++ en langage « assembleur ». C’est à dire, en instructions bas niveau, exécutable par le processeur ciblé. A noter que  chaque fabricant de micro-processeur a son propre code assembleur.

Niveau intermédiaire

L’image ci dessous montre un exemple de cette conversion du langage C vers un un langage assembleur:Dans l’exemple ci dessous le code C nous permet de lire facilement l’évolution des variable ax, bx et cx.c_assembleurLe code assembleur lui, est plus « détaillé ».
Le « CMP » signifie qu’on COMPARE  AX à la valeur 1, ensuite on « JNZ » (Jump if Not Zero) au « Else ».
C’est à dire que si la comparaison de AX et de 1 ne renvoi pas un Zéro (donc elle est fausse), le microprocesseur doit « JUMP » (donc sauter) à la ligne du Else. Si ce n’est pas le cas (la comparaison de AX et de 1 renvoi un Zéro), il continue normalement à exécuter la ligne suivante. Ensuite il affecte des valeurs aux variables avec l’opération « MOV » pour MOVE (bouger). Les variables déclarées en C, que vous nommez comme bon vous semble doivent passer dans les registres du microprocesseur pour être modifiées.
Si vous nommé dans votre code C, une variable « TOTO », la conversion de votre code C en assembleur fera apparaitre que votre variable « TOTO » passera dans le registre Accumulateur pour être modifié par le micro-processeur.Les registres AX, BX, CX et DX sont les registres les plus utilisés pour les calculs :
  • Le registre AX sert à effectuer des calculs arithmétiques ou à envoyer un paramètre à une interruption
  • Le registre BX sert à effectuer des calculs arithmétiques ou bien des calculs sur les adresses
  • Le registre CX sert généralement comme compteur dans des boucles
  • Le registre DX sert à stocker des données destinées à des fonctions

A mon humble avis, il n’est pas absolument nécessaire d’un connaitre plus sur le processeur pour commencer à  programmer un micro-contrôleur. Vous pouvez le considérer comme une entité capable d’exécuter des instructions assembleur.
Si toute fois, vous souhaitez en savoir un peu plus sur les structures internes, je vous propose d’aller un peu plus loin dans le paragraphe ci dessous.


Pour aller plus loin…

Examinons maintenant la structure interne et les éléments de base d’un processeur.

processeur_

  • Les registres sont des petites mémoires internes très rapides, pouvant être accédées facilement. Parmi les registres on trouve notamment
    • l’accumulateur qui sert à stocker les données traitées par l’UAL (l’unité de calcul arithmétique et logique, ALU en anglais)
    • compteur ordinal ou PC (Program Counter) donne l’adresse mémoire de la prochaine instruction
    • le pointeur de pile : il sert à stocker l’adresse des structures de données généralement utilisées pour gérer des appels de sous-programmes
    • le registre d’instruction : il permet quant à lui de stocker l’instruction en cours de traitement,
    • le registre d’état : il est composé de plusieurs bits, appelés drapeaux (flags), servant à stocker des informations concernant le résultat de la dernière instruction exécutée
    • les registres généraux, qui servent à stocker les données allant être utilisées (ce qui permet d’économiser des aller-retours avec la mémoire RAM).
  • L’unité de contrôle, se charge de gérer le processeur. Il peut décoder les instructions, choisir les registres à utiliser, gérer les interruptions ou initialiser les registres au démarrage.
  • L’UAL (Unité Arithmétique et Logique) est chargée de faire l’opération. on trouve différents types d’UAL :
    • Les UAL élémentaires calculent sur des nombres entiers, et peuvent effectuer les opérations communes, que l’on peut séparer en 4 groupes :
      • Les opérations arithmétiques : addition, soustraction, changement de signe, etc.
      • les opérations logiques : compléments à un, à deux, et, ou, ou-exclusif, non, non-et, etc.
      • les comparaisons : test d’égalité, supérieur, inférieur, et leur équivalents « ou égal ».
      • éventuellement des décalages et rotations (mais parfois ces opérations sont externalisées).
    • Les  UAL spécialisées dans la manipulation des nombres à virgule flottante, en simple ou double précision (on parle d’unité de calcul en virgule flottante (UVF, en anglais floating-point unit, FPU) ou dans les calculs vectoriels. Typiquement, ces unités savent accomplir les opérations suivantes :
      • additions, soustractions, changement de signe ;multiplications, divisions ;
      • comparaisons ;
      • modulos.
  • 3 Types de bus de communications sont utilisés:
    • bus de données, pour les accès à la mémoire, la taille de ce bus conditionne la taille des données en entrés/sorties
    • bus d’adresse, qui permet, lors d’une lecture ou d’une écriture, de sélectionner le registre à configurer, la taille de ce bus conditionne le nombre de cases mémoire accessibles ;
    • bus de contrôle, qui permet la gestion du matériel, via les interruptions.

Regardons ce qui se passe dans le processeur lors de l’exécution d’une instruction assembleur.

Les instructions sont décomposées en micro-instructions, souvent on trouve 4 étapes pilotées par l’unité de contrôle :

  • Fetch, recherche de l’instruction dans la mémoire flash du µC, à l’aide du PC (Conteur programme ou conteur ordinal) qui stocke l’adresse de la prochaine instruction
  • Decode, interprétation de l’instruction (opération et opérandes). Découpe l’instruction en plusieurs parties telles qu’elles puissent être utilisées par d’autres parties du processeur.  Souvent, une partie d’une instruction, appelée opcode (code d’opération), indique l’opération à effectuer, par exemple une addition.
  • Execute, exécution de l’instruction, met en relation différentes parties du processeur pour réaliser l’opération souhaitée. Par exemple, pour une addition, l’unité arithmétique et logique (ALU) sera connectée à des entrées et une sortie
  • Writeback, écriture du résultat, écrit les résultats de l’étape d’exécution en mémoire.

Appliquons cette exemple à une instruction d’addition simple,

add_micro_instruction
Le « Fetch » va chercher dans la mémoire flash (la mémoire qui contient les instructions) la ligne d’assembleur : 

« ADDI  &r1, &r2, 350 »  : on souhaite rajouter 350 à la valeur dans le registre R2 et enregistrer ce résultat dans le registre r1
Le « Decode » va donner un « Opération code » par exemple « 001000 » pour l’addition et interpréter les adresses des registres demandés (00001 pour r1 et 00010 pour r2)

Le « Execute » envoi les données à l’ALU (qui effectue l’opération)

Le « Writeback« , écriture du résultat de l’étape d’exécution en mémoire (de l’ALU vers r1)
C’était un exemple pour une instruction utilisant l’ALU mais certains types d’instructions manipulent le compteur de programme (registre PC) plutôt que de produire directement des données de résultat. Ces instructions sont appelées des branchements (branch) et permettent de réaliser des boucles (loops), des programmes à exécution conditionnelle ou des fonctions (sous-programmes) dans des programmes…

Comment optimiser les temps d’exécutions ? Quelle architecture interne ?

Le processeur est cadencé par son horloge. Pour simplifier, on peut imaginer qu’une de ces micro-instructions est exécutée à chaque coup d’horloge.
Pour notre exemple du ATMEGA2560, on peut le cadencer à 16MHz soit un coup d’horloge toute les 62.5 nanosecondes …
Sur une génération plus récente en architecture ARM  comme par exemple le « Atmel SAM3X8E ARM Cortex-M3 » on atteint 84 MHz soit une horloge 5.25 fois plus rapide.
Au fil des générations, les constructeurs ont trouvé des méthodes permettant d’augmenter la cadence des processeurs.
L’une de ces innovation est l’architecture RISC (en anglais Reduced Instruction Set Computer) : « processeur à jeux d’instructions réduit ». Cette stratégie était bien adaptée à la réalisation des microprocesseurs. À la fin des années 1980 et au début des années 1990, elle concurrençait des architectures traditionnelles plus lourdes CISC (complex instruction-set computer).
En effet, les grandes différences de taille, de temps de décodage comme de temps d’exécution s’opposaient à des optimisations de type « pipe-line »
L’efficacité de l’architecture RISC est lié au principe du « pipe-line ».

Principe du « pipe-line » sur une chaine de montage de voiture

Le pipeline est un concept s’inspirant du fonctionnement d’une ligne de montage. Considérons que l’assemblage d’un véhicule se compose en trois étapes :

  1. Installation du moteur (20 minutes)
  2. Installation du capot (5 minutes)
  3. Pose des pneumatiques (10minutes)

Un véhicule dans cette ligne de montage ne peut se trouver que dans une seule position à la fois.  

Cas numéros 1 :

Si l’installation du moteur, du capot et des roues prennent respectivement 20, 5 et 10 minutes, la réalisation de trois véhicules prendra, s’ils occupent un à un toute la chaîne de production,  (20 + 5 + 10) × 3=105 minutes.

Cas numéros 2 (pipeline) :

Si on place un véhicule dans la chaîne de production dès que l’étage auquel le véhicule doit accéder est libre (principe du pipelining), le temps total pour réaliser les trois véhicules est de 75 minutes.

Principe du « pipe-line » appliqué au processeur

Reprenons un cycle de micro-instructions :
IF (Instruction Fetch)
ID (Instruction Decode)
EX (Execute)
MEM (Memory), dénote un transfert depuis un registre vers la mémoire
WB (Write Back)

Appliquons le principe du « pipe-line » avec : un cycle d’horloge= une micro-instruction.

nopipeline
Sans Pipe-line
fivestagespipeline
Avec Pipe-line (5 étages)

Sur l’exemple ci dessus, en moyenne, ou passe de 5 cycles pour une instruction  à 1.8 cycles pour une instruction avec un pipeline de 5 étages.

Dans le cas de notre µC ATMEGA2560, le processeur utilise bien le pipeline mais sur un seul étage.
Une génération plus récente de µC comme le  Atmel SAM3X8E ARM Cortex-M3 utilise un pipe-line de 3 étages.
Enfin un processeur de PC comme le Intel Pentium 4 Prescott a 31 étages.

L’architecture RISC, avec son jeux d’instruction réduit, permet des cycles d’horloge beaucoup plus court pour exécuter une micro-instruction. Combiné avec le principe du pipeline, elle contribue a l’augmentation  de la cadence des processeur et donc de leur puissance de calcul.

sources : https://fr.wikipedia.org/wiki/Processeur
https://fr.wikipedia.org/wiki/Pipeline_(architecture_des_processeurs)

Ce qu’il faut retenir :

Votre code écrit en langage haut niveau (C/C++) sera compilé par les outils logiciel du fabricant pour une certaine cible de micro-contrôleur afin de générer des instructions assembleurs. Les instructions assembleur décrivent le code bas-niveau c’est à dire le déplacement des variables, des entrés/sorties, le parcours de la mémoire de programme… Le micro-processeur, par lecture et exécution du code assembleur, fait le lien entre les instructions (software) et l’état physique du microcontrôleur (hardware). 

Les mémoires

Reprenons l’analogie avec le PC. Le PC/tablette/Smartphone  sur le quel vous êtes en train de lire ce tutoriel possède plusieurs types de mémoire : Disque dur, RAM.

Le disque dur stocke les données à long terme tandis que la mémoire RAM  stocke les variables des programmes en cours d’exécution.
Les mémoires sont adaptées à leurs utilisations : la RAM est très rapide d’accès mais le prix au Go revient cher, c’est l’inverse pour le disque dur (lent d’accès et peu cher au Go).

Sur le micro-contrôleur on trouve 3 types de mémoire : RAM (identique PC), EEPROM (équivalent disque dur du PC), Flash (spécifique au microcontrôleur).

La RAM
Comme sur le PC, elle servira à stocker les variables du programme. Chaque fois que vous faites une nouvelle déclaration de variables, cette dernière se retrouvera dans cette mémoire. Une caractéristique à ne pas négliger, cette mémoire est entièrement effacée lorsque l’alimentation du micro-contrôleur est coupée (on dit qu’elle est « volatile »).

L’EEPROM
Ensuite, on trouve une mémoire dite morte, l’EEPROM (Electrically Erasable Programmable Read-Only Memory). Elle est capable de stocker des informations même lorsqu’elle n’est plus alimentée (on parle de mémoire morte). Cette dernière est similaire au disque dur de votre ordinateur par son comportement et ses caractéristiques. La vitesse d’accès est moins élevée que la RAM. Dans un programme, elle servira par exemple à conserver des données entre deux allumages du produit.

La FLASH
Enfin, une dernière mémoire est la Flash. Elle a un rôle un peu particulier, elle sert à stocker le programme que vous téléchargez dans le microcontrôleur (vous vous souvenez le : code machine assembleur).
Elle retient donc les informations même lorsque l’alimentation est coupée (pas besoin de flasher le programme à chaque allumage !). Elle stocke aussi le  boot loader, un petit bout de code qui agit comme le BIOS de votre PC.

Le boot loader détecte au démarrage  du micro-contrôleur si l’on tente de programmer la carte (via une liaison série ou un port ICSP…) le cas échéant copiera les données dans la mémoire FLASH. On n’y stocke pas de données pendant l’exécution du programme. Cette mémoire sera écrite lors du « flash » du micro-contrôleur et ne sera qu’en lecture lors de l’exécution du code.

Si l’on reprend l’exemple du micro-contrôleur de AVR le ATMEGA2560, on y trouve : Flash Memory 256 Ko (avec 8 KB utilisé par le bootloader), SRAM 8 Ko EEPROM 4 Ko

Ce qu’il faut retenir : lors de la compilation de votre programme, en fonction de la cible (microcontrôleur) choisie, le logiciel sera capable vous dire les pourcentage de chaque type de mémoire utilisé et donc de vous indiquer si votre programme est compatible avec la cible. Après avoir converti le code en assembleur, il calcule donc les ressources hardware (mémoire) utilisées par votre programme.

Les périphériques

Reprenons l’analogie avec le PC. Pour l’utiliser, vous devez y connecter : un écran, un clavier, une souris, une carte réseau, une carte graphique…. Ce sont des périphériques d’entrées/sorties. Ils sont reliés via des connecteurs (USB, SATA, PCI…) à la carte mère du PC.

C’est le même principe avec le micro-contrôleur, il intègre différents périphériques pour interagir avec son environnement. Nous allons maintenant nous pencher sur l’architecture interne du micro-contrôleur. Je vais vous présenter étape par étape, comment lire le schémas bloc fournit par AVR pour le ATMEGA2560, notre exemple.

Nous venons de voir le rôle du processeur et des mémoires.

avr_core1

Au centre le « AVR cpu » : c’est  le micro processeur (AVR CPU), il a directement accès à 3 éléments :

  • La mémoire Flash (dont nous avons parlé)  pour lire les instructions assembleur
  • La mémoire SRAM (pour gérer les variables lors de l’exécution du code)
  • Le BUS DMA (Direct Memory Access). (la flèche double tout autour de l’image) C’est un bus qui permet au CPU d’accéder à tous les registres de tous les périphériques. Comme il s’agit d’un bus 8 bit, le CPU travaille aussi en 8bit : on a donc un µC 8bit.

On voit, maintenant que ce bus DMA communique avec les périphériques suivant :

  • EEPROM (dont nous avons parlé)

    avr_core2

  • Convertisseur AD (Analogique/Digital) (lui même avec le « internal bandgad » en entrée)
  • Analog comparator (lui aussi avec le « internal bandgad » en entrée)
  • Des Timer/counter
    • 4 de 16 bits (T/C3, T/C4,T/C5,T/C1)
    • 2 de 8 bit (T/C0,T/C2)
  • Un périphérique SPI (communication SPI)
  • Un périphérique TWI (Two Wire Interface) souvent utilisé pour une communication I2C

Les périphériques les plus importants ( A/D Convertisseur, Timer/Counter et USART) seront détaillés dans la suite de ce tutoriel.


Enfin, le schémas bloc complet:

atmega2560

Par rapport au schéma précédent sont rajoutés :
Les Ports de A à L  (soit 12 ports de 8 bits) qui sont les interfaces hardware, avec l’extérieure du composant. Elle permettent à chaque pin d’être configurable en entrée/sortie et donc de fournir ou recevoir du courant.

Les USART (Universal Asynchronous Receiver Transmitter) qui permettent de gérer les communication numériques avec l’extérieur.

Le Power suppervision bloc et le Oscillator Clock Generator  qui permettent de mettre en forme les alimentations et l’horloge nécessaires au bon fonctionnement du micro-contrôleur.

Le Watchdog est un dispositif qui permet la surveillance du micro-contrôleur. Il est conçu pour envoyer régulièrement en signal lorsque les opération μ micro-contrôleur se déroule normalement. En cas de blocage/ plantage au niveau du processeur ou autre, le signal n’est plus émit, on peut alors (via un autre composant externe au micro contrôleur) activer un RESET du microcontrôleur. En clair, on le redémarre lorsqu’il plante (encore une analogie avec le PC). have-you-tried1

Ce qu’il faut retenir : le processeur vient lire et interpréter les instructions qui se trouvent dans la mémoire flash. Pour se faire, il gère des variables dans la mémoire RAM et il commande des périphériques via le bus DMA.

Pour conclure ce premier chapitre je vous propose de voir un exemple de tout ce trafic avec un smartphone.

Que se passe t-il dans le cerveaux numériques lorsque, par exemple, j’active le bluetooth ?

J’appuie sur l’icone bluetooth de mon écran tactile. Le périphérique du microcontrôleur en charge de la gestion de l’écran va écrire dans un coin de sa mémoire ( de son registre) « ici l’utilisateur a appuyé à la position x,y de l’écran ».

Pendant ce temps le processeur « fait sa vie » et est occupé à exécuter d’autres instructions android ou Iphone… C’est à dire qu’il lit et exécute les instructions présentes dans sa mémoire flash.

Sauf que les programmeurs du smartphone ont prévu dans leur code de venir vérifier régulièrement (toutes les 100 millisecondes par exemple) le périphérique qui gère l’écran tactile. Lorsque cette instruction arrive, le processeur accède au registre qui gère l’écran tactile (via le bus DMA) et lit la valeur de son registre. Il constate alors que l’utilisateur a appuyé à la position x,y de l’écran tactile et il range cette valeur dans sa mémoire RAM.

Le processeur passe à l’instruction suivante de sa mémoire flash. Cette instruction lui dit que si l’utilisateur a appuyé sur l ‘écran, alors il doit comparer la position ou l’utilisateur à appuyé avec la position de l’icone bluetooth (entre autres). Et il range le résultat dans sa mémoire RAM.

Le processeur passe à l’instruction suivante de sa mémoire flash. Cette instruction lui dit que si la position appuyée est celle de l’icône bluetooth, alors il faut activer le bluetooth.

Le processeur passe à l’instruction suivante de sa mémoire flash. Et active le bluetooth en appliquant la bonne valeur au registre bluetooth (via le bus DMA).

Dans la partie suivante, nous apprendrons à manipuler ces périphériques afin interagir avec le monde extérieur.

Scroll Up