Manuel de laboratoire pour contrôleurs embarqués

Utilisation du langage C et de la plateforme Arduino


précédentsommairesuivant

V. Les structures conditionnelles

Dans cet exercice, on examinera l'utilisation des structures conditionnelles. Celles-ci comprennent les constructions if/else et switch/case. Les structures conditionnelles sont utilisées pour faire un choix, c'est-à-dire aiguiller le déroulement du programme dans différentes directions, en fonction des circonstances rencontrées. La construction if/else fonctionne bien s'il n'y a que deux actions possibles, alors que le switch/case est plutôt conçu pour aiguiller le programme selon une liste de possibilités établies. La structure conditionnelle la plus simple est le if(). Si le ou les éléments à tester sont évalués comme vrais, alors telle action est prise. En regroupant des clauses avec les opérateurs logiques || et &&, des tests complexes peuvent être créés. Les déclarations if() peuvent être imbriquées, et inclure le traitement pour l'échec du test en utilisant la déclaration else. Si vous devez choisir un seul élément dans une liste, par exemple lors du traitement d'un élément sélectionné dans un menu, cela peut être réalisé de la manière suivante :

 
Sélectionnez
if( choice == 1 )
{
   /* faire des choses pour le choix 1 */
}
else
{
      if( choice == 2 )
      {
         /* faire des choses pour le choix 2 */
      }
      else
      {
         if( choice == 3 )
         {
            /* faire des choses pour le choix 3 */
         }
         /* et ainsi de suite */
   }
}

Cet arrangement est un peu encombrant lors du choix dans une grande liste. En outre, il est difficile de traiter des choix multiples (par exemple, choix des éléments 1 et 3). Pour ces situations, le langage C offre la solution switch/case. Il n'y a rien qu'un switch/case peut faire que vous ne puissiez pas recréer avec des if/else imbriqués, mais la première solution offre une plus grande commodité ainsi qu'un code plus clair et plus compact. Le switch/case est utilisé fréquemment, mais en fin de compte, if/else est plus souple, car il ne se limite pas à choisir parmi une liste de valeurs numériques. Habituellement, les constantes numériques ne sont pas utilisées dans le code, comme dans l'exemple ci-dessus. Au lieu de cela, on utilise les directives #define pour les constantes afin de rendre le code plus lisible et intelligible. Une version switch/case de l'exemple précédent pourrait ressembler à :

 
Sélectionnez
#define WALK_DOG 1
#define LET_OUT_CAT 2
#define COMB_WOMBAT 3

switch( choice )
{
   case WALK_DOG:
      /* viens mon chien... */
      break;

   case LET_OUT_CAT:
      /* la porte est ... */
      break;
   
   case COMB_WOMBAT:
      /* D'abord le shampooing... */
      break;

   /* et ainsi de suite */
}

Dans cet exercice, nous allons utiliser les deux constructions. Le programme consiste à calculer des paramètres de polarisation DC pour des circuits à transistors simples. On donnera à l'utilisateur un choix de trois polarisations différentes (polarisation par diviseur de tension, polarisation d'émetteur, et polarisation par réaction de collecteur). Le programme demandera alors les valeurs des composants appropriés, déterminera le courant de collecteur au repos et la tension collecteur-émetteur. Il déterminera également si le circuit est en saturation. Ces valeurs seront présentées à l'utilisateur.

Une approche de ce problème est de traiter cela comme trois problèmes élémentaires liés entre eux. C'est-à-dire considérer ce qu'on doit faire pour une polarisation, puis répliquer le traitement avec les changements appropriés pour les deux autres. L'ensemble est ensuite lié avec un simple traitement de menu. Voici un pseudocode :

  1. Donner à l'utilisateur une direction appropriée et une liste de polarisations au choix ;
  2. Demander à l'utilisateur leur choix de la polarisation ;
  3. Aller à la routine appropriée pour la polarisation choisie. Pour chaque polarisation :

    1. demander les valeurs nécessaires des composants (résistances, alimentation, bêta),
    2. calculer Ic et Vce et déterminer si le circuit est en saturation,
    3. présenter les valeurs à l'utilisateur.

Les équations appropriées pour chaque polarisation sont les suivantes. Toutes les polarisations utilisent ce qui suit : Vcc est la source d'alimentation positive. Re est la résistance de l'émetteur tandis que Rc est la résistance du collecteur. Beta est le gain en courant (hfe). La tension base-émetteur (Vbe) peut être supposée égale à 0,7 volt. Notez que si Ic-saturation est supérieure à Ic, alors l'Ic actuel est égal à Ic-saturation et Vce sera égale à 0.

Polarisation par diviseur de tension : nécessite également R1, R2 (résistances supérieure et inférieure du diviseur).

Image non disponible

Vth = Vcc*R2/(R1+R2)

Rth = R1*R2/(R1+R2)

Ic = (Vth-Vbe)/(Re+Rth/beta)

Vce = Vcc-Ic*(Re+Rc)

Ic-saturation = Vcc/(Rc+Re)

Polarisation par réaction de collecteur : nécessite également Rb (résistance de base).

Image non disponible

Ic = (Vcc-Vbe)/(Re+Rc+Rb/beta)

Vce = Vcc-Ic*(Re+Rc)

Ic-saturation = Vcc/(Rc+Re)

Polarisation d'émetteur : nécessite également Vee (alimentation négative de l'émetteur) et Rb (résistance de base).

Image non disponible

Ic = (Vee-Vbe)/(Re+Rb/beta)

Vce = Vee+Vcc-Ic*(Re+Rc)

Ic-saturation = (Vee+Vcc)/(Rc+Re)

Vee est une valeur absolue dans tous les cas.

Le programme est découpé en plusieurs morceaux, présentés ci-dessous dans l'ordre d'écriture. D'abord, le squelette principal :

 
Sélectionnez
#include <stdio.h>
#include <math.h>
#define VOLTAGE_DIVIDER 1
#define EMITTER 2
#define COLLECTOR_FEEDBACK 3
#define VBE 0.7

int main( void )
{
   int choice;
   give_directions();
   choice = get_choice();

   switch( choice )
   {
      case VOLTAGE_DIVIDER:
         voltage_divider();
         break;

      case EMITTER:
         emitter();
         break;

      case COLLECTOR_FEEDBACK:
         collector_feedback();
         break;

      default: /* Prevenir l'utilisateur qu'il n'a pas brille sur ce coup-la... */
         printf("Mauvais choix !\n");
         break;
   }
}

Les deux premières fonctions pourraient ressembler à ceci (n'oubliez pas que vous devrez ultérieurement ajouter leur prototype) :

 
Sélectionnez
void give_directions( void )
{
   printf("Polarisation de transistors en courant continu\n");
   printf("Calcul du point de repos Q\n\n");
   printf("Choix de la polarisation :\n");
   printf("1. Par diviseur de tension\n");
   printf("2. Par l'emetteur avec deux sources de tension\n");
   printf("3. Par reaction de collecteur\n");
}

int get_choice( void )
{
   int ch;
   printf("Entrez le numero de votre choix :");
   scanf("%d", &ch);
   return( ch );
}

Maintenant, il est temps d'écrire les fonctions de calcul de polarisation. Voici à quoi la fonction voltage_divider() pourrait ressembler :

 
Sélectionnez
void voltage_divider( void )
{
   double vcc, vth, r1, r2, rth, re, rc, beta, ic, icsat, vce;
   printf("Entrez la tension de l'emetteur en volt");
   scanf("%lf", &vcc);
   printf("Entrez le gain en courant (beta ou hfe)");
   scanf("%lf", &beta);
   printf("Toutes les valeurs de resistance doivent être saisies en ohm\n");
   printf("Entrez la valeur de la resistance superieure du diviseur");
   scanf("%lf", &r1);
   printf("Entrez la valeur de la resistance inferieure du diviseur");
   scanf("%lf", &r2);
   printf("Entrez la valeur de la resistance du collecteur");
   scanf("%lf", &rc);
   printf("Entrez la valeur de la resistance de l'emetteur");
   scanf("%lf", &re);

   vth = vcc*r2/(r1+r2);
   rth = r1*r2/(r1+r2);
   ic = (vth-VBE)/(re+rth/beta);
   icsat = vcc/(rc+re);
   
   if( ic >= icsat )
   {
      printf("Le circuit est en saturation !\n");
      printf("Ic = %lf amp et Vce = 0 volt\n", icsat );
   }
   else
   {
      Vce = vcc-ic*(re+rc);
      printf("Ic = %lf amp et Vce = %lf volt\n", ic, vce );
   }
}

Les deux autres fonctions de polarisation devraient être similaires à celle-ci. Quelques points à noter : afin d'obtenir la valeur absolue, envisagez d'utiliser la fonction fabs() (valeur absolue d'un nombre en virgule flottante). Une autre approche du programme serait de rendre les variables vce, icsat et ic comme des variables globales, et de déplacer la section d'affichage finale dans le programme principal main(), car la comparaison finale et l'affichage seront identiques dans les trois fonctions de calcul de polarisation. Il est également possible que les fonctions renvoient les valeurs via des pointeurs, en évitant le besoin de variables globales.

Complétez les deux autres fonctions de polarisation, créez et testez le programme. Pour éviter les avertissements du compilateur, vous devez placer les prototypes de fonction avant le programme principal main(). Vous pouvez les placer dans un fichier d'en-tête séparé, mais ce n'est pas vraiment nécessaire. Pour créer les prototypes, il suffit de copier et coller les déclarations de fonction, et d'ajouter un point-virgule à la fin. Par exemple :

 
Sélectionnez
#define VBE .7

void give_directions( void );

int get_choice( void );
/* et ainsi de suite */



void main( void )
{
   ...

Le compilateur ne « connaîtra » pas les fonctions utilisées dans le programme principal main(), ce qu'elles prennent comme arguments, ni quel type de variable elles retournent si leur prototype n'est pas déclaré auparavant. Sans prototype, le compilateur suppose un argument void par défaut et s'attend à une valeur de retour de type int. En découvrant l'appel de la fonction, le compilateur vous avertira alors que les types ne correspondent pas aux types attendus. Ce mécanisme croisé de vérification peut sembler redondant, mais il peut empêcher de nombreux bogues.

Modifications

Ce programme pourrait être utile à un étudiant apprenant les transistors, mais il peut être un peu lourd à utiliser. Après tout, le programme doit être redémarré à chaque nouveau circuit. En d'autres termes, l'utilisateur y gagnerait en confort s'il pouvait démarrer le programme et l'exécuter continuellement pour 100 circuits s'il le souhaite. Comment modifieriez-vous le programme afin de demander à l'utilisateur s'il souhaite ou non essayer un autre circuit ? Peut-être plus intéressant, qu'est-ce qui serait nécessaire pour que le programme puisse servir à la génération d'« exercices » ? C'est-à-dire pour que le programme puisse créer des circuits appropriés à l'aide de nouvelles valeurs de composants à chaque fois et en informer l'utilisateur. Il demanderait ensuite à l'utilisateur de calculer le courant et la tension à la main et de les saisir. Le programme déterminerait alors si les réponses de l'utilisateur sont correctes (avec un certain pourcentage d'erreur pour compenser les erreurs d'arrondi). Une autre idée serait de présenter à l'utilisateur une liste à choix multiples de réponses possibles, au lieu de lui faire entrer des valeurs précises. Enfin, comment pouvez-vous essayer de « dessiner » les circuits afin que les composants soient visuellement identifiés ?


précédentsommairesuivant

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Licence Creative Commons
Le contenu de cet article est rédigé par James M. Fiore et est mis à disposition selon les termes de la Licence Creative Commons Attribution - Pas d’Utilisation Commerciale - Partage dans les Mêmes Conditions 3.0 non transposé.
Les logos Developpez.com, en-tête, pied de page, css, et look & feel de l'article sont Copyright © 2013 Developpez.com.