Même si les microcontrôleurs de la famille MSC12xx ne peuvent rivaliser avec les composants logiques programmables spécialisés (FPGAs, etc.), il est utile d'évaluer précisément les performances en fréquence que l'on peut obtenir sur les sorties numériques.
Cette section traite de l'utilisation des ports «classiques» du microcontrôleur. Les périphériques spécialisés tels que les timers ou le port SPI font l'objet de sections particulières.
On présente donc un exemple de programme qui permet d'évaluer les différents types de temporisations logicielles ainsi que le temps d'exécution des différentes boucles du Langage C. Le code source du programme complet est disponible sous le nom : sdcc_port_timings.c.
Pour chaque exemple de temporisation logicielle, on présente le code en Langage C, le code assembleur correspondant ainsi que les mesures de temps pour un appel unique et pour une exécution supplémentaire.
Remarque importante : les mesures temps ont été réalisées à l'aide d'un microcontrôleur MSC1210Y5 cadencé à 22.1184MHz.
L'instruction nop (acronyme de no
operation) est une instruction assembleur dont le seul but est de
gaspiller un cycle d'horloge du microcontrôleur. En appelant cette instruction
directement à partir du langage C on doit obtenir le plus petit temps
d'exécution possible.
Codage en Langage C :
#define nop() {_asm nop _endasm;}
TEST_PIN = 0;
nop();
TEST_PIN = 1;
Code assembleur produit par le compilateur sdcc :
; sdcc_port_timings.c:79: TEST_PIN = 0; clr _TEST_PIN ; sdcc_port_timings.c:80: _asm nop _endasm; nop ; sdcc_port_timings.c:81: TEST_PIN = 1; setb _TEST_PIN
Temps mesuré à l'analyseur logique :
Après avoir mesuré le plus petit temps d'exécution, on utilise une boucle
de type do-while codée en assembleur et insérée dans le code
source du programme en langage C. Cette technique, baptisée
assembleur en ligne, est sensée optimiser la compilation
relativement au code généré par le compilateur. Si l'optimisation manuelle est
efficace, le temps d'exécution sera plus réduit qu'avec un programme écrit en
langage C.
Codage en Langage C :
void inline_delay (unsigned char n) __naked {
n;
_asm
mov r0, dpl
00001$: nop
djnz r0, 00001$
ret
_endasm;
}
// dans le programme principal
ser_puts("Temporisation assembleur en ligne: ");
ser_gets_echo(chaine, MAXCHAR);
v = atoi(chaine);
TEST_PIN = 0;
inline_delay(v);
TEST_PIN = 1;
Code assembleur produit par le compilateur sdcc :
;------------------------------------------------------------
;Allocation info for local variables in function 'inline_delay'
;------------------------------------------------------------
;n Allocated to registers
;------------------------------------------------------------
; sdcc_port_timings.c:37: void inline_delay (unsigned char n) __naked {
; -----------------------------------------
; function inline_delay
; -----------------------------------------
_inline_delay:
; sdcc_port_timings.c:44: _endasm;
mov r0, dpl
00001$:
nop
djnz r0, 00001$
ret
; naked function: no epilogue.
// dans le programme principal
; sdcc_port_timings.c:98: TEST_PIN = 0;
clr _TEST_PIN
; sdcc_port_timings.c:99: inline_delay(v);
lcall _inline_delay
; sdcc_port_timings.c:100: TEST_PIN = 1;
setb _TEST_PIN
Temps mesuré à l'analyseur logique :
L'appel du sous-programme
inline_delay( dure 2.890µs et
l'incrémentation d'une unité de la variable n)n ajoute 720ns
au temps d'exécution du sous-programme.
À partir de cette étape, on n'utilise plus d'artifice d'optimisation et
on s'appuie entièrement sur le travail du compilateur. Une boucle
while doit avoir un temps d'exécution plus court que les autres
types de boucles sachant qu'elle utilise moins d'opérations.
Codage en Langage C :
void while_delay (unsigned char n) {
while (n--)
;
}
// dans le programme principal
ser_puts("Temporisation boucle tantque: ");
ser_gets_echo(chaine, MAXCHAR);
v = atoi(chaine);
TEST_PIN = 0;
while_delay(v);
TEST_PIN = 1;
Code assembleur produit par le compilateur sdcc :
;------------------------------------------------------------
;Allocation info for local variables in function 'while_delay'
;------------------------------------------------------------
;n Allocated to registers r2
;------------------------------------------------------------
; sdcc_port_timings.c:47: void while_delay (unsigned char n) {
; -----------------------------------------
; function while_delay
; -----------------------------------------
_while_delay:
mov r2,dpl
; sdcc_port_timings.c:48: while (n--)
00101$:
mov ar3,r2
dec r2
mov a,r3
jnz 00101$
ret
// dans le programme principal
; sdcc_port_timings.c:114: TEST_PIN = 0;
clr _TEST_PIN
; sdcc_port_timings.c:115: while_delay(v);
mov dpl,r2
lcall _while_delay
; sdcc_port_timings.c:116: TEST_PIN = 1;
setb _TEST_PIN
Temps mesuré à l'analyseur logique :
L'appel du sous-programme
while_delay( dure 5.040µs et
l'incrémentation d'une unité de la variable n)n ajoute 1.280µs
au temps d'exécution du sous-programme.
Comme dans le cas précédent, on s'appuie entièrement sur le compilateur.
Sachant qu'une boucle for effectue davantage d'opérations
relativement à la boucle while, le temps d'exécution doit être
plus important que dans tous les cas précédents.
Codage en Langage C :
void for_delay (unsigned char n) {
__idata unsigned char i;
for (i = 0; i < n; i++)
;
}
// dans le programme principal
ser_puts("Temporisation boucle tantque: ");
ser_gets_echo(chaine, MAXCHAR);
v = atoi(chaine);
TEST_PIN = 0;
for_delay(v);
TEST_PIN = 1;
Code assembleur produit par le compilateur sdcc :
;------------------------------------------------------------
;Allocation info for local variables in function 'for_delay'
;------------------------------------------------------------
;i Allocated to registers r3
;n Allocated to registers r2
;------------------------------------------------------------
; sdcc_port_timings.c:55: void for_delay (unsigned char n) {
; -----------------------------------------
; function for_delay
; -----------------------------------------
_for_delay:
mov r2,dpl
; sdcc_port_timings.c:59: for (i = 0; i < n; i++)
mov r3,#0x00
00101$:
clr c
mov a,r3
subb a,r2
jnc 00105$
inc r3
sjmp 00101$
00105$:
ret
// dans le programme principal
; sdcc_port_timings.c:136: TEST_PIN = 0;
clr _TEST_PIN
; sdcc_port_timings.c:137: for_delay(v);
mov dpl,r2
lcall _for_delay
; sdcc_port_timings.c:138: TEST_PIN = 1;
setb _TEST_PIN
Temps mesuré à l'analyseur logique :
L'appel du sous-programme
for_delay( dure 5.790µs et
l'incrémentation d'une unité de la variable n)n ajoute 1.790µs
au temps d'exécution du sous-programme.
Vous êtes ici :