V. Les structures conditionnelles▲
Dans cet exercice, on examinera l'utilisation des structures conditionnelles. Celles-ci comprennent les instructions 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 :
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 en cas de 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 des directives #
defin
e
pour les constantes afin de rendre le code plus lisible et intelligible. Une version switch/case de l'exemple précédent pourrait ressembler à  :
#
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
la
.
.
.
*/
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 :
- Donner à l'utilisateur une direction appropriée et une liste de polarisations au choix ;
- Demander à l'utilisateur son choix de la polarisation ;
-
Aller à la routine appropriée pour la polarisation choisie. Pour chaque polarisation :
- demander les valeurs nécessaires des composants (résistances, alimentation, bêta),
- calculer Ic et Vce et déterminer si le circuit est en saturation,
- 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. Bêta 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).
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).
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).
Ic = (Vee-Vbe)/(Re+Rb/beta)
Vce = Vee+Vcc-Ic*(Re+Rc)
Ic-saturation = (Vee+Vcc)/(Rc+Re)
Où 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 :
#
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) :
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 :
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 semblables à 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 :
#
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 ?