Vous êtes sur la page 1sur 35

Lart de programmer son petit copeau de silicium

Quatrime anne DGEI INSA / release 2010c

Automne 2010

Table des matires


1 On 1.1 1.2 1.3 1.4 en tient une couche Introduction et dnitions . . . . . . Architecture logicielle recommande Guide des bonnes manires . . . . . La couche matrielle . . . . . . . . . 5 5 5 6 7 8 8 8 10 12 12 12 15 16 17 17 18 18 20 20 20 20 20 21 22 22 23 23

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

2 Faudrait pas prendre les pilotes pour des navigateurs ! 2.1 Premier exemple : conguration dune broche dentre-sortie 2.1.1 Pilote dynamique . . . . . . . . . . . . . . . . . . . . 2.1.2 Pilote statique . . . . . . . . . . . . . . . . . . . . . 2.2 Second exemple : conguration dune interruption . . . . . . 2.2.1 Pilote dynamique . . . . . . . . . . . . . . . . . . . . 2.2.2 Pilote statique . . . . . . . . . . . . . . . . . . . . . 3 Bah ! Les masques 3.1 Oprateur logiques et bit bit 3.2 Mettre un bit 1 . . . . . . . . 3.3 Mettre un bit 0 . . . . . . . . 3.4 Inverser un bit . . . . . . . . . 3.5 Initialiser une tranche de bits .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

4 Lpointeur de fonctions ne manque(nt) pas de piquant ! 4.1 Appel indirect des fonctions . . . . . . . . . . . . . . . . . . 4.1.1 Pointeur de fonction . . . . . . . . . . . . . . . . . . 4.1.2 Aectation dun pointeur de fonction . . . . . . . . . 4.1.3 Appel de fonction par un pointeur . . . . . . . . . . 4.1.4 Passage de fonctions en paramtre . . . . . . . . . . 4.2 Lart et la manire... . . . . . . . . . . . . . . . . . . . . . . 4.2.1 ... de sen servir avec la solution dynamique . . . . . 4.2.2 ... de sen passer avec la solution statique . . . . . . 4.2.3 Quelques commentaires pour se faire une religion . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

5 Une mise au point simpose ! 25 5.1 Les points darrts et les espions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 5.1.1 Les dirents points darrts . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 2

5.2

5.1.2 Pose dun point darrt . . . . . . . Emulation avance . . . . . . . . . . . . . 5.2.1 Contrle de lexcution . . . . . . . 5.2.2 Simulation de lactivit des broches

. . . . . . du

. . . . . . . . . . . . . . . STM32

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

26 27 27 30

Chap 1:
1.1

On en tient une couche

Introduction et dnitions

Un pilote informatique (ou plus connu sous langlicisme driver ) est un programme informatique qui permet un autre programme dinteragir avec un priphrique. En gnral, chaque priphrique a son propre pilote. Pour assurer la qualit dune application, il est ncessaire de bien concevoir les pilotes et leur relation avec lapplication. Petit rappel : en informatique, la qualit dsigne un ensemble dindicateurs pour orir une apprciation globale dun logiciel. Elle se base sur : la compltude des fonctionnalits, la prcision des rsultats, la abilit, la tolrance aux pannes, la facilit et la exibilit de son utilisation, la simplicit, lextensibilit, etc. Parmi ces critres, le nologisme portabilit dsigne, pour un programme informatique, sa capacit fonctionner dans dirents environnements dexcution, en particulier dirents environnements matriels. A cela sajoute ladaptabilit qui est souvent utilis pour dsigner la qualit dun logiciel qui peut tre modi aisment en harmonie avec les changements auxquels son utilisation peut tre soumise. La portabilit et ladaptabilit dune application ncessite davoir une architecture logicielle solide. Celle-ci dnit lorganisation interne dun logiciel, son dcoupage en couches et en modules, ainsi que les responsabilits de chaque module et la nature et la structure des relations entre modules. Les relations entres les modules sont fournies via une interface de programmation (Application Programming Interface ou API ). Elle permet linteraction des programmes les uns avec les autres. Du point de vue technique une API est un ensemble de fonctions, procdures ou classes mises disposition par une bibliothque logicielle, un systme dexploitation ou un service. La connaissance des API est indispensable linteroprabilit entre les composants logiciels. Dans le cas typique dune bibliothque, il sagit gnralement de fonctions considres comme utiles pour dautres composants. Une interface en tant que telle est quelque chose dabstrait ; les composants ralisant celle-ci tant des mises en uvre (ou implmentation). Idalement il peut y avoir plusieurs mises en uvre pour une mme interface. Par exemple, sous UNIX, la libc dnit des fonctions de base utilises par pratiquement tous les programmes et est fournie par des mises en uvre propritaires ou libres, sous dirents systmes dexploitation.

1.2

Architecture logicielle recommande

Pour en revenir nos moutons lectroniques, nous prconisons pour les TP de priphriques une architecture logicielle en trois couches (voir gure 1.1). Les direntes couches sont : La couche application : Cette couche ne comporte que du code ddi une application. Elle comporte lensemble des fonctions de traitement et dorchestration de lapplication. La couche de services applicatifs : Cette couche un ensemble de fonctions qui masquent les priphriques matriels au niveau de lapplication pour en orir une reprsentation abstraite. 5

application

couche application

module vitesse

module position

...

module supervision

module comm.

couche services applicatifs

module ADC

module PWM

...

module IO

module Timers

couche pilotes

Figure 1.1 Architecture logicielle

Par exemple, le service dacquisition dune vitesse masque lappel au priphrique ADC pour orir une vision purement abstraite de la variable de vitesse. La couche de pilotes : Cette couche ne comporte que du code li la conguration et lutilisation des priphriques sans prsuppos une utilisation particulire par une application. Une telle architecture ore de nombreux avantages : Seules les couches services applicatifs et pilotes doivent changer si le matriel volue, la couche application nayant a priori besoin que de quelques modications pour prendre en considration les nouvelles capacits du support dexcution (surtout en ce qui concerne le temps). Les modules qui composent la couche pilotes peuvent tre utilises pour direntes applications et ainsi orir des composants pris sur tagres. La couche services applicatifs fournit des services gnriques pour un mme ensemble de familles dapplications sans avoir besoin dtre rcrit chaque dveloppement ou volution dune application sur le mme support. Le test des modules est fait de manire unitaire, ce qui facilite le dbogage sans avoir besoin de chercher la petite bte. Les lments qui composent une couche ne peuvent communiquer quavec un lment dune couche infrieure et cela se fait uniquement travers les API des direntes modules.

1.3

Guide des bonnes manires

Chaque module est associ deux chiers, lun comportant le code (.c) et lautre dnissant son API (.h). Seul les fonctions dclares dans le cher den-tte sont accessibles aux autres lments logiciels. Comme le chier den-tte est le seul lment utilisable (lisible / modiable) par un utilisateur externe au module, il est capital de bien le commenter. Le commentaire est considr comme un 6

mode demploi du module. Ceci est dautant plus vrai, que les constructeurs de micro-contrleurs proposent des modules (ou bibliothques) au format .lib ou .o. Ces derniers sont compils donc de fait, non modiables. An dassurer la portabilit des modules de la couche pilotes, il est interdit quune des fonctions dun module utilisent une variable globale au projet. Si on veut utiliser une variable globale au projet, dans la couches service ou application, cela peut se faire par le biais dun chier dentte partag, par exemple, global.h. Ceci tant, cest fortement dconseill. Quelques rgles sont spciques aux couches : Couche application Le main() est contenu dans la couche application et ne peut faire appel qu des fonctions de la couche services. Aucune rfrence des priphriques nest fait au niveau de la couche application. Couche services applicatifs Le code des modules de la couches services contient des fonctions directements lies lapplication : les noms de fonctions et de variables font explicitement rfrence lapplication. Les fonctions au niveau services appellent uniquement des fonctions de niveau pilotes. Elles ne sappellent pas entre elles. Dit autrement, elles ne peuvent inclure que des chiers de niveau pilote. Les chiers sont organiss par domaine li lapplication Couche pilotes La couche pilotes ne contient que les fonctions qui concernent directement les priphriques. Les noms des fonctions doivent voquer les priphriques. Les fonctions ne font pas rfrence lapplication et doivent tre dnie de manire indpendante toute application.

1.4

La couche matrielle

Le logiciel, et dans notre cas la couche pilote, est excut sur le coeur du processeur et doit communiquer avec les priphriques. Les priphriques sont des circuits, essentiellement de llectronique numrique, prsents sur la puce qui fournissent des fonctions dinterface avec le matriel sur lequel la puce est embarque. Ces circuits ne sont pas programmables comme le coeur : ils ne peuvent pas excuter des lignes de programmes. Par contre, on peut les congurer, c--d. modier un paramtre de son fonctionnement, o transfrer des donnes avec le coeur du processeur. TODO SCHEMA REGISTRE GPIOA_ODR Si lon crit une valeur ladresse XXX, avec la ligne *(0x8000123)=13 en langage C qui sera traduite par exemple par ldr R0,=0x8000123 ; ldr R1,=13; str R1,[R0] en assembleur, le coeur va positionner la valeur 0x8000123 sur le bus dadresse, la valeur 13 sur le bus de donne, la ligne R/W 0 pour write et attendre un front dhorloge actif. TODO nir tout a

Chap 2:

Faudrait pas prendre les pilotes pour des navigateurs !

Lors de limplmentation dun pilote plusieurs possibilits sont oertes en particulier pour tout ce qui est li la conguration.

2.1

Premier exemple : conguration dune broche dentre-sortie

Prenons lexemple de la conguration dun port dentre-sortie. Au cour de la conception la fonction Pin_IO_Init(State, Port, Pin_Number) est dcrite comme permettant au pin numro Pin_Number du port Port dtre congurer dans ltat State. Au niveau implmentation cela peut se traduire de deux manires : soit dynamiquement, cest-dire que cest lexcution que la conguration sera faite, soit statiquement, cest--dire que la conguration sera faite la compilation. Ces deux stratgies ncessitent alors une implmentation dirente.

2.1.1

Pilote dynamique

Dans le cas dynamique, lAPI du pilote fournit lapplication une fonction de conguration, par exemple lAPI ./pilotes/GPIO_dyn.h du listing 2.1 et son implmentation dans ./pilotes/GPIO_dyn.c // Port c o n f i g u r a t i o n f u n c t i o n void Pin_IO_Init ( char S t a t e , GPIO_TypeDef Port , int Pin_Number ) ; // S t a t e s h o u l d be i f o r INPUT, o f o r OUTPUT // Port p o i n t e r t o GPIO s t r u c t u r e d e f i n e d i n stm_regs . h : GPIOA, . . . // Pin_Number : as i t s a y s . . . // Example : // Pin_IO_Init ( o ,GPIOD, 3 ) ; s e t p o r t D p i n number 3 as an o u t p u t Listing 2.1 ./pilotes/GPIO_dyn.h : lAPI du pilote dynamique (listing 2.2) permet de rgler les registre du Port voulus en fonction de State et Pin_Number. La conguration dune broche se fera par un appel de lapplication (couche application ou service) en passant les paramtres souhaits la fonction Pin_IO_Init. Dans lexemple du listing 2.3, on rgle la broche 2 du port GPIOA en entre. Les fonctions de congurations sont donc crites dans le chier .c, par exemple ./pilotes/GPIO_dyn.c et on donne accs aux couches suprieures (service et application) via une API dument commente, par exemple ./pilotes/GPIO_dyn.h. Larborescence des chiers conseille, voir g. 2.1, permet ainsi de rutiliser le pilote dans plusieurs projets sans faire de copie multiple de ces chiers. 8

void Pin_IO_Init ( char S t a t e , GPIO_TypeDef Port , int Pin_Number ) { i f ( S t a t e== i ) { // i n p u t p o r t // r e s e t mode [ 0 : 1 ] t o 00 = f l o a t i n g i n p u t Port >CRL &= ~(3<<(Pin_Number 4 ) ) ; } else { // o u t p u t p o r t // s e t mode [ 0 : 1 ] t o 11 = o u t p u t 50MHz Port >CRL |= (3<<(Pin_Number 4 ) ) ; } } Listing 2.2 ./pilotes/GPIO_dyn.c : source du pilote dynamique

#include " . . / . . / . . / p i l o t e s /GPIO_dyn . h" // ou #i n c l u d e "GPIO_dyn . h" s i l e chemin // e s t d c l a r dans l e s o p t i o n s du c o m p i l a t e u r . . . ... void main ( void ) { Init_Clock ( ) ; Init_GPIO ( ) ; I n i t _ P i n ( i ,GPIOA, 2 ) ; // Bouton V a l i d de MCB167 } Listing 2.3 ./projet/tes_GPIO_dyn/Test_GPIO.c : source applicative

Figure 2.1 Arborescence o le rpertoire pilotes est au mme niveau que celui des projets. Plusieurs projets peuvent donc inclure le mme pilote en donnant le chemin relatif vers le .h (../../pilotes/GPIO_stat.h). Chaque applicatif de projet (Test_GPIO.c) congure son pilote en passant les paramtres lors d elappel de la fonction dinitialisation. Dans le cas de pilote statique, le chier de conguration GPIO_stat_conf.h est plac dans le projet car la conguration dpend de lapplication et ne peut tre la mme.

2.1.2

Pilote statique

Dans le cas statique, la conguration se fait directement dans un chier de conguration, par exemple ./projet/exemple/GPIO_stat_conf.h, appartenant au projet. Dans ce chier de conguration, lutilisateur va commenter ou dcommenter des directives #define ... : //CONF : i f you want t o use GPIOA p o r t t h e n // uncomment t h e f o l l o w i n g l i n e #define GPIOA_IS_USED // CONF : by d e f a u l t a l l p o r t s a r e i n p u t s // t o s e t a p o r t t o o u t p u t add l i n e s i n t h e f o r m a t // #u n d e f P<p o r t >_<pin> // #d e f i n e P<p o r t >_<pin> IS_OUTPUT // Example : PB_3 f o r t p i n number 3 o f GPIOB #undef PA_2 #define PA_2 IS_OUTPUT Listing 2.4 ./projet/exemple/GPIO_stat_conf.h : conguration via des directive de compilation Le chier de conguration est inclus par le chier source, par exemple ./pilotes/GPIO_stat.c, ce qui va inuencer sa compilation et ainsi intgrer les congurations : Lapplication, quand elle, ne fait quappeler une fonction dinitialisation sans paramtres : Dans cet exemple la simplicit nest pas en faveur de la version statique, cependant le code 10

#define IS_INPUT 0 x0 #define IS_OUTPUT 0 x3 #define PA_0 IS_INPUT #define PA_1 IS_INPUT #define PA_2 IS_INPUT ... #include " GPIO_stat_conf . h" // p a t h t o t h e c o n f f i l e s h o u l d be s e t i n c o m p i l e r o p t i o n s // Now PA_x a r e c o n f i g u r e d by u s e r void GPIO_Init ( void ) { #i f d e f GPIOA_IS_USED // c o n s t r u c t t h e i n i t from t h e PA_x c o n f i g u r e d GPIOA>CRL = (PA_0<<0) | ( PA_1<<4) | ( PA_2<<8) | ( PA_3<<12) | ( PA_4<<16) | ( PA_5<<20) | ( PA_6<<24) | ( PA_7<<28) ; #e n d i f } Listing 2.5 ./pilotes/GPIO_stat.c : congur via des directives de compilation

#include " . . / . . / . . / p i l o t e s /GPIO_stat . h" // ou #i n c l u d e "GPIO_stat . h" s i l e chemin e s t dans l e s o p t i o n s . . . ... void main ( void ) { Init_Clock ( ) ; Init_GPIO ( ) ; } Listing 2.6 ./projet/exemple/main_stat.c : source applicative

11

produit statiquement est beaucoup moins coteux en terme de mmoire, car il ne compilera les fonctions de conguration que si des ports sont utiliss.

2.2

Second exemple : conguration dune interruption

Les interruptions peuvent permettre dexcuter une fonction lie lapplication suite un vnement matriel. Il y a donc dun ct la fonction qui appartient la couche applicative et de lautre linterruption capturer au niveau de la couche pilotes. Lors de la conguration du systme il faut tre capable de faire le lien entre ces deux lments...

2.2.1

Pilote dynamique

Dans sa version dynamique, cela revient prototyper une fonction dans le pilote du priphrique qui permet de faire le lien entre la fonction et une interruption du priphrique. Supposons que nous voulons excuter la fonction Gazabeuh(void) lors de linterruption provoque par le priphrique XXX. Le pilote de XXX comportera une fonction Init_IT_XXX(void (* IT_fonction) (void)) qui permettra de lier linterruption de XXX la fonction IT_fonction. void ( pt_IT_Hook ) ( void ) ; // P o i n t e u r de f o n c t i o n pour l IT void Init_IT_XXX ( void ( I T _ f o n c t i o n ) ( void ) ) { pt_IT_Hook = I T _ f o n c t i o n ; . . . // A c t i v e r l i n t e r r u p t i o n } ... Listing 2.7 ./pilotes/Pilote_XXX.c : conguration dune interruption Le handler de linterruption est alors dni dans la suite de manire gnrique par la code suivant : Au niveau applicatif la conguration du priphrique se fera grce au code suivant : Bien sr la fonction de conguration de linterruption pourrait tre plus volue, par exemple en xant le niveau de linterruption etc.

2.2.2

Pilote statique

Dans le cas dun pilote statique la solution est plus simple implmenter. Il sut de faire un chier de conguration : Le source du pilote utilise la directive #ifdef pour insrer ou non lappel de la fonction lors de linterruption Remarquez que dans cet exemple, la version statique va gnrer uniquement un Handler vide alors que la version dynamique gnre un Handler avec 4 lignes de code, plus une fonction dinitialisation (1 ligne) et son appel du main (1 ligne). Vous comprenez maintenant la compacit de la version statique mais malheureusement aussi sa complexit... 12

... void XXX_IRQHandler void // Handler de l IT de XXX { i f ( ( int ) pt_IT_Hook != 0 ) { ( pt_IT_Hook ) ( ) ; // Appel l a f o n c t i o n } else while ( 1 ) ; // b o u c l e i n f i n i . . Le h a n d l e r n e s t pas c o n f i g u // mais i l e s t d a n g e r e u x de f a i r e n i m p o r t e q u o i } Listing 2.8 ./pilotes/Pilote_XXX.c : handler dune interruption en dynamique

#include " . . / . . / . . / p i l o t e s / Pilotes_XXX . h" ... void main ( void ) // Handler de l IT de XXX { Init_Clock ( ) ; Init_GPIO ( ) ; Init_IT_XXX(&Gazabeuh ) ; } void Gazabeuh ( void ) { // Code e x c u t e r pendant l i n t e r r u p t i o n } ... Listing 2.9 ./projet/ex_it/main.c : Exemple de conguration dune interruption en dynamique

// CONFIGURATION PART // p l e a s e uncomment i f you wich t o l a u n c h a f u n c t i o n on IT // #d e f i n e THERE_IS_A_HOOK_ON_IT #i f d e f THERE_IS_A_HOOK_ON_IT // CONFIGURE h e r e t h e f u n c t i o n p r o t o t y p e void Gazabeuh ( void ) ; //CONFIGURE h e r e t h e c a l l t o t h e hook f u n c t i o n #d e f i n e IT_HOOK_CALL Gazabeuh ( ) #endif Listing 2.10 ./projet/exemple/Pilote_XXX_conf.h : conguration via des directives de compilation

13

#include " Pilote_XXX_conf . h" // p a t h t o t h e c o n f f i l e s h o u l d be s e t i n c o m p i l e r o p t i o n s void XXX_IRQHandler void // Handler de l IT de XXX { #i f d e f THERE_IS_A_HOOK_ON_IT IT_HOOK_CALL; #e l s e while ( 1 ) ; // b o u c l e i n f i n i . . Le h a n d l e r n e s t pas configu // mais i l e s t d a n g e r e u x de f a i r e n i m p o r t e q u o i #end ... } Listing 2.11 ./pilotes/Pilote_XXX.c : handler dIT utilisant les directives de compilation

vous de choisir...

14

Chap 3:

Bah ! Les masques

Pour programmer un priphrique il est ncessaire daller modier un ou plusieurs bits dans un registre sans modier les autres. Par exemple le registre ADC_CR1 sert congurer le convertisseur analogiquenumrique ADC1 :

Figure 3.1 Extrait du reference manual du STM32 p. 236, la suite dcrit la fonction de chaque bit, comme SCAN et AWDIE et la signication des valeurs de chaque tranches de bit tels que DISCNUM

Pour cela on utilise les masques logiques et certaines astuces pour construire un masque clairement sans risquer de se tromper. Dans lexemple suivant le bit SCAN de ADC_CR1 est mis 1 et le bit EOCIE est mis 0 sans toucher aux autres bits. Les registres ADC1_CR2, ADC1_SQR1, ADC1_SQR3 sont aussi manipuls avec des masques : ADC1 >CR1 ADC1 >CR1 |= (ADC_SCAN) ; &= ~(ADC_EOCIE) ; // c o n t i n u o u s scan o f c h a n n e l s 1 , 1 4 , 1 5 // pas d i n t e r r u p t i o n de f i n de conv .

ADC1 >CR2 |= (ADC_EXTSEL_ON_SWSTART | ADC_ CONT | ADC_DMA) ; // EXTSEL = SWSTART // use d a t a a l i g n r i g h t , c o n t i n u o u s c o n v e r s i o n // send DMA r e q u e s t // c o n v e r t s e q u e n c e i s c h a n n e l 1 t h e n 14 t h e n 15 ADC1 >SQR3 |= ( 1 <<SQ1_SHIFT) | ( 1 4 <<SQ2_SHIFT) | ( 1 5 <<SQ3_SHIFT) ; Listing 3.1 Extrait de la conguration de lADC de la baguette magique vue en assembleur 15

Si vous avez aucune ide de comment ce code fonctionne cest que vous ne maitrisez pas encore lart primitif du masque : lisezdonc la suite. Cicontre, un masque primitif (on prfre dire dart premier) dorigine Gabonaise.

3.1

Oprateur logiques et bit bit

On utilise les oprateurs logiques ET, OU, XOR (OU exclusif) et NOT entre un registre et un masque pour manipuler les bits. Les oprateurs logiques et leurs syntaxe en langage C sont rsums dans le tableau suivant :

Fonction logique ET OU XOR NON

Oprateur logique && || aucun !

Oprateur bit bit & |

Loprateur logique considre les oprandes (quelle soient 8/16/32 bit) comme une valeur boolenne fausse si tous les bits sont nuls et vrai sinon. Elle fournit un rsultat boolen nul si cest faux et dirent de zro (en gnral la valeur 1) sinon. Ne confondez donc pas loprateur logique avec loprateur bit bit qui eectue 8/16/32 oprations logiques entre chaque bits respectifs des oprandes, par exemple : a b y z = = = = 2 ; // s o i t b00000010 en b i n a i r e e s t v r a i c a r d i f f r e n t de 0 1 ; // s o i t b00000001 en b i n a i r e e s t v r a i a u s s i a && b ; // donne 1( v r a i ) c a r a ET b e s t v r a i a & b ; // donne b00000000 c a r chaque ET d e s b i t s de a e t b s o n t faux

16

3.2

Mettre un bit 1

Pour cela on utilise loprateur OU avec une valeur binaire, appele masque, ayant des bits 1 uniquement devant les bits que lon veut initialiser : b7 b6 b5 b4 b3 b2 b1 b0 0 0 0 1 0 0 1 1 b7 b6 b5 1 b3 b2 1 1

OU =

car

x OU 1 = 1

et

x OU 0 = x

Ainsi les bits b4 , b1 et b0 sont passs 1 sans modier la valeur des autres bits. Pour eectuer cela en langage C on doit calculer la valeur du masque binaire : convertir b00010011 en hexadcimal (0x13) ou en dcimal (19) car le langage C nadmet pas de littraux en binaire. char a v o i l e ; ... // f o r m u l a t i o n s q u i v a l e n t e s a v o i l e = a v o i l e | 0 x13 ; // o p r a t e u r | i n l i n e a v o i l e |= 0 x13 ; // o p r a t e u r | p r f i x = a v o i l e |= 1 9 ; // v a l e u r du masque en d c i m a l Il nest pas trs vident de comprendre que 0x13 ou 19 correspond un masque visant les bits de rang 0,1 et 4, de plus il est trs facile de se tromper lorsque lon fait la conversion soimme. Un geek utilisera loprateur de dcalage gauche <<x pour positionner un 1 devant le bit de rang x pour construire sont masque ainsi : b7 b6 b5 b4 b3 b2 b1 b0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 1 1

OU OU =

(1 << 0) (1 << 1) (1 << 4) (1 << 0)|(1 << 1)|(1 << 4)

Ainsi le code suivant est plus lisible et ne risque pas de comporter derreur de conversion : // f o r m u l a t i o n s q u i v a l e n t e s a v o i l e = a v o i l e | 0 x13 ; // o p r a t e u r | i n l i n e a v o i l e = a v o i l e | (1<<0)|(1 < <1) |(1 < <4) ; a v o i l e |= (1<<0)|(1 < <1) |(1 < <4) ;

3.3

Mettre un bit 0

Pour cela on utilise loprateur ET avec une masque ayant des bits 0 uniquement devant les bits que lon veut initialiser : b7 b6 b5 b4 b3 b2 b1 b0 1 1 1 0 1 1 0 0 b7 b6 b5 0 b3 b2 0 0 17

ET =

car

x ET 1 = x

et

x ET 0 = 0

Ainsi les bits b4 , b1 et b0 sont passs 0 sans modier la valeur des autres bits. Pour construire le masque, on peut toujours eectuer la conversion soit-mme avec un risque derreur : b11101100 = 0xEC = 236. On peut aussi construire le masque avec des 1 devant les bits annuler et ensuite inverser chaque bit avec loprateur .

ET =

avoile &

(1 << 0)|(1 << 1)|(1 << 4) (1 << 0)|(1 << 1)|(1 << 4)

0 1

0 1

0 1

1 0

0 1

0 1

1 0

1 0

avoile b7 b6 b5 b4 b3 b2 b1 b0 ((1 << 0)|(1 << 1)|(1 << 4)) b7 b6 b5 0 b3 b2 0 0

Ce qui donne en langage C : // f o r m u l a t i o n s q u i v a l e n t e s a v o i l e = a v o i l e & 0xEB ; a v o i l e = a v o i l e & ~((1<<0) |(1 < <1) |(1 < <4) ) ; a v o i l e &= ~((1<<0) |(1 < <1) |(1 < <4) ) ;

3.4

Inverser un bit

Pour cela on utilise loprateur XOR avec une masque ayant des bits 1 uniquement devant les bits inverser : b7 b6 b5 b4 b3 b2 b1 b0 XOR 0 0 0 1 0 0 1 1 = b7 b6 b5 b4 b3 b2 b1 b0

car

x XOR 1 = x

et

x XOR 0 = x

Ainsi seuls les bits b4 ,b1 et b0 ont t inverss. On construit les masques comme les masque OU de mise 1.

3.5

Initialiser une tranche de bits

Une tranche de bit est un ensemble de quelques bits contigus dont la valeur a une signication particulire. Par exemple DISCNUM est une tranche de 3 bits du registre ADC_CR1 qui indique le nombre de canaux convertir. Un programmeur avertis peut dsirer initialiser une tranche de avoile la valeur contenue dans numero. Pour cela il faut procder en trois tapes : limiter la valeur de numero pour ne pas dpasser la tranche de 3 bits et la caler au bon endroit ; annuler la tranche de trois bits de avoile ; puis 18

recopier les 1 du premier masque dans avoile avec un OU : num 0x7 (num & 0x7) (num & 0x7) << 4 (0x7 << 4) avoile & (avoile & n7 n6 n5 n4 n3 n2 n1 n0 0 0 0 0 0 1 1 1 0 0 0 0 0 n2 n1 n0 0 n2 n1 n0 0 0 0 0 b7 0 0 0 b3 b2 b1 b0 n2 n1 n0 b3 b2 b1 b0

OU =

(0x7 << 4)) | (num & 0x7) << 4) b7

Le programme suivant permet daecter la tranche DISCNUM avec la valeur contenue dans la variable numero : void set_discnum ( char numero ) { // r a z de l a t r a n c h e DISCNUM : 3 b i t s de rang 13 15 de ADC_CR1 ADC >CR1 &= ~(0 x7<<13) ; // s e u l s l e s b i t s 13 15 du masque v a l e n t 0 // i n i t de l a t r a n c h e a v e c numero ADC >CR1 |= numero<<13 // r e c o p i e l e s 1 de numero d c a l au rang 13 // A t t e n t i o n s i numero >7 a d b o r d e s u r l e s b i t s 16 31 //Un masque d o i t l i m i t e r numero de 0 7 : pas p l u s de t r o i s b i t s 1 // On p r f r e donc c e t t e l i g n e ADC >CR1 |= ( numero & 0 x7 ) <<13 // numero e s t l i m i t }

19

Chap 4:
4.1

Lpointeur de fonctions

ne manque(nt) pas de piquant !


Appel indirect des fonctions

Extraits de : T. Monteil et al., Du langage C au C++, Presses Universitaires du Mirail, 2009. De la mme manire que le nom dun tableau reprsente ladresse dimplantation de ce tableau (cest--dire ladresse de son premier lment), le nom dune fonction (sans parenthses) reprsente ladresse de cette fonction, plus exactement ladresse de son point dentre. Cette adresse peut tre : attribu un pointeur (pointeur de fonction), ce qui permet lappel indirect de cette fonction, transmise comme paramtre dautres fonctions, place dans un tableau de pointeurs de fonction.

4.1.1

Pointeur de fonction

Un pointeur de fonction est une variable pouvant recevoir ladresse dune fonction. Dans la dclaration du pointeur, cette fonction est type, si bien quil ne peut ensuite accepter de recevoir que ladresse dune fonction de mme type. Voici la forme gnrale de la dclaration dun tel pointeur : type ( * ptrFct ) (type, type, ...); Les parenthses de gauche sont obligatoires, sinon il sagirait dune fonction retournant un pointeur.

4.1.2

Aectation dun pointeur de fonction

Voici comment on aecte ladresse dune fonction un pointeur : double ( * Math ) ( int, int, double ) ; /* Pointeur de fonction */ double Racine ( int, int, double ) ; /* Fonction */ Math = Racine ; /* Affectation */

4.1.3

Appel de fonction par un pointeur

Ds quun pointeur reu ladresse dune fonction, on peut appeler celle-ci indirectement selon la forme gnrale suivante : /* dfinition dun pointeur */ type ( * ptrFcn ) (type, type, ...); 20

/* dclaration dune fonction de mme type renvoy */ type Fonc (type, type, ...); /* affectation du pointeur */ ptdFcn = Fonc; /* appel indirect de Fonc() */ (*ptrFcn) (arguments); Dans le cas prsent, les deux instructions ci-dessus sont bien quivalentes un appel direct de Fonc() puisque *ptrFcn a pour valeur Fonc.

4.1.4

Passage de fonctions en paramtre

Il est possible de transmettre une fonction en paramtre dune fonction appelante. Celle-ci emploie le nom de la fonction appele sans parenthses ni arguments la suite. Cest donc ladresse du point de lancement de cette fonction qui est empile. Pareillement, on peut transmettre indirectement une fonction en paramtre grce un pointeur sur cette fonction. Dans lexemple qui suit, Fonction() est appele deux fois avec deux (adresses de) fonctions direntes passes en argument, et une fois avec un pointeur de fonction : int Premiere (void) ; int Seconde (void) ; int ( * pFonction ) () = Seconde ; void Fonction (int (* ptrFoncArg) (), int Un , int deux ) { /* Corps de Fonction () */ } /* exemple dappels */ /************************************************************************************/ /* Appel directement avec ladresse de la fonction Premier et les entiers 12 et -45 */ Fonction ( Premiere, 12, -45); /************************************************************************************/ /* Appel avec ladresse de la fonction Premier et les entiers 0 et 0 */ Fonction ( Seconde, 0, 0); /************************************************************************************/ /* Appel indirect avec ladresse de la fonction Seconde contenue */ /* dans le pointeur de fonction */ /* Les deux entiers sont affect avec la sortie des fonctions Premiere et Seconde */ Fonction ( pFonction, Premiere (), Seconde () ); 21

4.2

Lart et la manire...

Pour bien comprendre lutilit de cette technique de programmation, il convient den rappeler lobjectif principal : nous voulons orir le moyen un dveloppeur dapplication de congurer un priphrique sans quil est besoin den connatre son fonctionnement. Le code que produit le dveloppeur est uniquement dans la couche application (voir chapitre 1) et il ne peut faire appel qu des fonctions de la couche pilote pour manipuler le matriel. Il se pose alors le problme suivant : comment orir un service un dveloppeur dapplications qui lui permette dexcuter lors dune interruption une fonction quil aura dveloppe ? Il est noter que cette approche est quivalente la notion de services quore un systme dexploitation. Par exemple, il souhaite excuter la fonction : void Ma_Fonction_IT ( void ) /* Couche application */ { /* Le code excuter pendant linterruption */ } Il faut noter que, quoiquil en soit, cette fonction ne pourra ni prendre darguments en entre ni en retourner puisque son appel ne sera pas dclencher logiciellement. Il faut donc que dans le Handler de linterruption (la fonction qui sera appele en premier lieu lors de linterruption) il fasse appel cette fonction, soit : void XXX_IRQHandler ( void ) { Ma_Fonction_IT (); } /* Couche pilote */

Or cela ne respecte pas nos rgles de codage, puisque le Handler doit faire partie de la couche pilote alors que Ma_foncion_IT fait partie de la couche application... Il ny a pas de solution miracle, il faut faire un peu de programmation avance ! Ca fait du bien aux neurones... Remarquons au passage que la solution est fournie dans le chapitre 2. Si dautres solutions eectives sont proposes, nous sommes preneur.

4.2.1

... de sen servir avec la solution dynamique

Une premire solution consiste utiliser un pointeur de fonctions et scrit : /* Fichier de la couche "application" */ void Ma_Fonction_IT ( void ) { /* Le code excuter pendant linterruption */ } /* faire dans le main */ Init_Periph(Ma_Fonction_IT);

22

/* Fichier de la couche pilote */ void (* pFnc) (void) ; /* dclaration dun pointeur de fonction */ void XXX_IRQHandler ( void ) { if (pFnc != 0) (*pFnc) (); /* appel indirect de la fonction */ } Init_periph (void (* ptrFonction) (void)) { pFnc = ptrFonction; /* affectation du pointeur */ }

4.2.2

... de sen passer avec la solution statique

Une seconde solution consiste dnir dans un chier de conguration la fonction excuter pendant linterruption : /* Fichier de la couche application crite par lutilisateur du pilote*/ void Ma_Fonction_IT ( void ) { /* Le code excuter pendant linterruption */ } /* Fichier de configuration modifiable par lutilisateur du pilote*/ #ifdef HOOK_ON_XXX #define XXX_HOOK_CALL Ma_Fonction_IT () #endif /* Fichier de la couche pilote uniquement visible par le dveloppeur du pilote */ #ifdef HOOK_ON_XXX void XXX_IRQHandler ( void ) { XXX_HOOK_CALL ; } #end

4.2.3

Quelques commentaires pour se faire une religion

Ces deux solutions sont lgantes et permettent de bien sparer les couches application et pilote. Elles ne sont pas strictement identiques au niveau fonctionnel et comportent quelques nuances : La version dynamique permet de congurer linterruption la vole et de la recongurer en cours dexcution. La version statique ncessite une compilation du chier pilote et donc de fournir son code, ce qui nest pas le cas de la version dynamique. De plus, il faut maintenir le chier de conguration. 23

La version statique va gnrer uniquement un Handler sil y a une fonction excuter alors que la version dynamique gnre un Handler avec 4 lignes de code, plus une fonction dinitialisation (1 ligne) et son appel du main (1 ligne), ce qui permet davoir un code plus compact.

24

Chap 5:
simpose !
5.1
5.1.1 Les dirents points darrts

Une mise au point

Les points darrts et les espions

Pour mettre au point vos programmes en assembleur en 3e anne, vous avez utilis des points darrt dexcution. Il sut de placer un tel point darrt sur une instruction pour que lexcution se suspende lorsque ladresse de cette instruction est atteinte par le compteur ordinal du STM32. On a alors tout loisir pour observer les donnes et les registres, et, ventuellement, les modier avant de reprendre lexcution. Vision ore dautres possibilits que nous allons rapidement passer en revue. 1. Tout point darrt dispose dun systme de comptage (count) qui fait que le point darrt ne devient actif que sil a t atteint le nombre de fois indiqu. Par dfaut, count=1, et le point darrt fonctionne chaque fois que ladresse est atteinte. Si, par exemple, count vaut 3, les 2 premiers passages lendroit du point darrt ne dclencheront rien, mais lexcution sinterrompra au 3e, puis 4e, 5e, 6e, 7e, passage. 2. Lorsquun point darrt actif est atteint, le dbogueur peut aussi lancer une commande choisie au pralable par lutilisateur. Une fois la commande nie, lexcution du programme reprend, sans marquer darrt (le point "darrt" est alors plutt un point de dtournement vers le moniteur). 3. Outre le point darrt dexcution, il existe deux autres types de points darrts : le point darrt daccs la mmoire : le point darrt est actif si une variable, quil faut bien sr spcier lors de la dnition du point darrt, vient dtre utilise par une instruction. On peut demander ragir tout type daccs, ou ltrer en ne considrant que les accs en lecture ou ceux en criture. On peut aussi demander de ne ragir que si la variable prend une certaine valeur. le point darrt conditionnel : on associe cette fois au point darrt une expression conditionnelle qui sera automatiquement teste en permanence (aprs lexcution de chaque instruction). Si la condition devient vraie, le point darrt est atteint. N.B. Contrairement au point darrt dexcution, ces deux derniers points darrt ne sont pas lis une instruction spcique. En rsum, on peut dire quassocies chaque point darrt, on trouve trois informations appeles expression, compte (count) et commande. Selon que lexpression, obligatoire, est une instruction (code) : on a un point darrt dexcution ; une variable, ou une expression boolenne sur la valeur dune variable, avec laccs READ, ou WRITE, ou READWRITE : on a un point darrt daccs ; une expression boolenne ou une variable sans mention daccs : on a un point darrt conditionnel. La commande est facultative, comme nous lavons vu. 25

5.1.2

Pose dun point darrt

Il exite deux possiblits pour poser un point darret : soit avec la commande spcique BS dans la fentre de dialogue Output Windows, soit travers une fentre de gestion associe. La commande BS Placer un point darrt dexcution ordinaire est facile : il sut de cliquer deux fois sur un espace de la ligne dinstruction, ou dutiliser le bouton de dpose. Pour placer un point darrt plus labor, on peut faire appel la commande du dbogueur BREAKSET (ou BS, cf. la documentation en ligne Vision Users Guide : Debug Commands). Elle doit tre tape dans la ligne de commande du dbogueur, ou place dans un chier dinitialisation .ini. Voici la syntaxe de cette commande : BS [READ | WRITE | READWRITE] expression [, compte] [, "commande"] ainsi que quelques exemples : Commande au dbogueur BS main BS \Timer_1234 \50 BS Port_IO_Blink 4 BS TicTac==1 BS READWRITE Heure BS WRITE Test==100,1, " printf(\ "Le test vaut 100 \\n\")" BS WRITE Carac < 65 BS Carac < 65 "MaFonction(4)" E E E C A A A C Arrt sur la 1re instruction de la fonction main() sur linstruction la ligne 175 du module Horloge partir du 4e appel la Port_IO_Blink()(1re instruction) si le boolen TicTac devient vrai sur tout accs la variable Heure seulement si Test 100, lancement dune commande (printf() fonc. prdnie) si Carac reoit une valeur infrieure 65 si Carac est < 65, lance la fonction MaFonction() avec largument 4 fonction

Remarques : 1. Dans le dernier exemple, la commande eectue sous condition consiste en lappel dune fonction de mise au point dnie par lutilisateur, en lui fournissant largument 4. 2. Notez aussi que les deux derniers exemples sont dnis avec exactement la mme expression boolenne, Carac < 65, et pourtant, le premier correspond un point darrt daccs, alors que le second est un point darrt conditionnel. 3. Notez galement que la vitesse de simulation est considrablement diminue par lutilisation de points darrt conditionnels. Pour la surveillance de variables dterministes, il vaut donc mieux employer des points darrt sur accs. En revanche, la surveillance des pattes du STM32 exigera lemploi de points darrt conditionnels. 4. Toutes ces commandes ne sont taper quune seule fois : lorsque lon fait des allers/retours entre ldition et le debug, ces informations sont conserves. 26

Figure 5.1 Fentre de gestion des points darrts

Pose dun point darrt grce la fentre de gestion des points darrt Pour un dbutant, lutilisation de la commande BS nest pas trs facile. Il est alors plus simple de faire appel la fentre de gestion des points darrts, appele par Debug Breakpoints,... (Voir gure 5.1) Pour poser un nouveau point darrt dans cette fentre, vous devez au moins y fournir lexpression dnissant ladresse daccs, et, ventuellement, modier le compte, spcier laccs en mmoire ainsi que le nombre dobjets (ou doctets) concerns partir de ladresse daccs, et spcier une commande. La gure suivante reprsente cette fentre de gestion, au moment o lon sapprte dnir le dernier point darrt du tableau prcdent (conditionnel car aucune des cases Access na t coche). Les autres points darrt prsents correspondent aussi ceux de ce tableau dexemples.

5.2

Emulation avance

Le logiciel de dveloppement que vous utilisez ore des possibilits de contrle de lexcution du programme qui permettent davoir une grande matrise de ce qui passe. Nous distinguerons dans ce paragraphe deux aspects particuliers qui sont le contrle de lexcution et le contrle des activits sur les broches du circuit

5.2.1

Contrle de lexcution

Pour avoir une comprhension complte et une action possible de lexcution dun programme en mode simul le logiciel donne accs des variables du systme. Attention il ne faut pas les confondre avec des registres du microcontrleur (mme si certaines dentre elles apparaissent comme tel dans le fentre register en mode debug). Ces variables vous permettent, depuis une fonction ou 27

une commande, dobtenir des informations sur ltat de votre programme lorsquun point darrt est atteint. Elles permettent aussi de contrler la reprise de lexcution. Vous les trouverez listes en totalit dans le documentation Vision Users Guide -> Debbuging Voici les trois variables systmes les plus utiles : nom _break_ states type unsigned int unsigned long accs R/W R Description sommaire Arrte lexcution si sa valeur est dirente de 0 (voir son utilisation au paragraphe suivant). Valeur courante du compteur dtats (coups dhorloge) de la CPU. Ce compteur est remis 0 au dbut de toute excution. Valeur courante du pointeur de programme. On peut changer sa valeur pour eectuer un saut dans le code.

unsigned long

R/W

Remarques : La variable states se dcline en variable sec en convertissant le nombre de coups dhorloge en seconde par la simple multiplication la priode de lhorloge CPU. Ces variables permettent donc davoir un mesure trs prcise du temps dexcution qui sera coul en mode rel. La variable _break_ permet quant elle davoir une inuence sur le droulement de lexcution de lmulation. Quand une fonction ou une commande est appele lorsquun point darrt a t atteint, vous pourrez remarquer que normalement, lexcution reprend sans marquer darrt. Il est possible dobtenir quand mme un arrt, en programmant la fonction pour quelle change la valeur de la variable _break_. Nous allons voir quelques exemples de fonctions dnies par lutilisateur, en supposant quon a plac un point darrt devant ragir tout accs en criture au registre GPIOB_ODR : BS WRITE GPIOB_ODR. Le comportement dun tel point darrt dpend de la commande associe Sil ny a aucune commande prvue, lexcution stoppe chaque criture dans loctet de poids faible du port 3. Si lon associe au point darrt la fonction suivante : FUNC void SansArret (void) { if ((PORTB & 0x0200) == 0) /* PORTB est un VTREG : on teste la borne GPIOB.9 */ printf ("La borne = GPIOB.9 est 0 !\n") ; else printf ("Bip\n") ; } Pour cette, on a employ le VTREG PORTB (cf.paragraphe 5.2.2 pour la comprhension des VTREG). Ainsi on observe ce qui se passe au niveau de lactivit lectrique des broches (PORTB) et non pas au niveau de lactivit logique du registre sens la piloter (GPIOB_ODR). Ceci permet de conrmer, par exemple, que la conguration de direction est bien positionne. Lexcution du programme ne sinterrompt pas, mais chaque fois que le point darrt est atteint, on a un message 28

qui sache dans la fentre de messages du dbogueur : les niveaux bas de la borne 9 du port GPIOB sont identis par un message particulier. Changeons de fonction : FUNC void AvecArret (void) { if ((PORTB & 0x0200) == 0) /* PORTB est un VTREG : on teste la borne GPIOB.9 */ { printf ("La borne = GPIOB.9 est 0 !\n") ; _break_ = 1 ; /* variable systme, globale, permanente */ } else printf ("Bip\n") ; } Tant que le bit 9 nest pas 0, lexcution continue avec achage du message "Bip" chaque criture dans le port GPIOB (pf). Puis lexcution sinterrompt systmatiquement pour toute autre modication du port GPIOB. Avec lexemple suivant, lexcution sarrte chaque criture avec le bit 3 0 : FUNC void SemiArret (void) { if ((PORTB & 0x0200) == 0) /* PORTB est un VTREG : on teste la borne GPIOB.9 */ { printf ("La borne = GPIOB.9 est 0 !\n") ; _break_ = 1 ; /* arrt */ } else { printf ("Bip\n") ; _break_ = 0 ; /* pas darrt */ } } Nous avons vu que tout point darrt pouvait ne devenir actif quaprs avoir t atteint un certain nombre de fois, grce la proprit de comptage (count). Avec _break_, on peut raliser lopration duale : fabriquer un point darrt qui ne fonctionne quun certain nombre de fois, en dbut de lexcution. Par exemple : 29

FUNC void Arret_ X _ fois (void) { if ((PORTB & 0x0200) == 0) /* PORTB est un VTREG : on teste la borne GPIOB.9 */ { printf ("La borne = GPIOB.9 est 0 !\n") ; if (_break_ != 0) /* ou if ( !_break_) */ _break_ = _break_ - 1 ; } } Avant de lancer lexcution, il faut initialiser la variable _break_ en tapant, par exemple, la commande _break_ = 5. Lexcution sarrtera alors 4 fois, puis ne sarrtera plus.

5.2.2

Simulation de lactivit des broches du STM32

Lorsque lon cherche mettre au point un programme pour un microcontrleur en mode simul, une des dicults majeures est de pouvoir agir (et observer) sur ce quil se passe au niveau des broches du circuit. Cela induit que lon soit capable de simuler galement lenvironnement extrieur et donc de se mettre dans des conditions de travail qui sapproche le plus possible des conditions relles dexprimentation. Cette simulation des interactions aux bornes du STM32 peut seectuer de deux manires : la premire mthode, rudimentaire, consiste utiliser les fentres ouvertes par le menu droulant Peripherals. Remarque : souvenez-vous que sous simulation, vous ne bnciez de la mise jour rgulire des fentres du dbogueur que si vous avez coch loption View Periodic Window Update. La seconde mthode, plus puissante, fait appel une vritable programmation simulant le monde extrieur laide de fonctions (que nous appellerons fonctions de lutilisateur), crites en un langage C rduit, contenues dans un chier dinitialisation dont le nom prsente le suxe .ini. Ces fonctions - fonctions de mise au point et fonctions signal - sont destines au dbogueur, et ne font pas partie du programme pour le STM32, mme sil est dusage dinclure ce chier .ini dans le projet. On indique au dbogueur de prendre en compte ces fonctions en citant le nom de ce chier dans le menu Project Options for Target Xxx Debug Initialization File ou bien en utilisant la commande au dbogueur INCLUDE (cf. dernier paragraphe). Les fonctions de lutilisateur Ces fonctions, destines au dbogueur, sont indpendantes du programme mettre au point. Elles permettent, comme nous venons de le dire, la simulation du monde extrieur. Pour les crire, lutilisateur emploie la syntaxe du langage C-ANSI, quelques restrictions prs (cf 5.2.2). Si elles envoient des signaux - gnralement priodiques - vers les pattes dentre des priphriques du STM32 on les appelle fonctions signal ; elles nentrent en fonction que si elles ont t lances en tapant leur nom dans la ligne de commande du dbogueur. Les fonctions de mise au point, elles, permettent dobserver ltat lectrique des pattes de sortie des priphriques du STM32 et permettent aussi dobserver et de modier tous les registres du STM32, ainsi que toutes les variables de son programme. Dans ce cas, elles sont souvent associes un point darrt, cest--dire quune certaine 30

fonction de mise au point nest lance que si une condition darrt a t atteinte par le programme (cf. les points darrt) . Dautre part, pour Vision, il existe en fait deux classes de fonctions : les fonctions dnies par lutilisateur, celles dont nous venons de parler, et les fonctions prdnies, cest--dire faisant partie de la bibliothque fournie avec le dbogueur (built-in functions). Avant de dtailler ces fonctions, voyons comment seectue linterface entre les deux simulateurs.

Les bornes du STM32 (VTREG) Pour avoir accs aux bornes du microcontrleur, il faut que les fonctions crites par lutilisateur puissent les dsigner. Pour cela, ltat lectrique des pattes des ports du STM32 est gard dans des registres spciaux appels VTREG (Virtual Target Register). Ces registres sont externes au STM32 et font partie du simulateur. Ils peuvent contenir des valeurs analogiques (entres du convertisseur analogique-numrique ADC) ou numriques. En voici quelques-uns : VTREG PORTA PORTB PORTC PORTD ADC1_INy ADC2_INy SxIN SxOUT SxTIME Description E/S numriques E/S numriques E/Snumriques E/S numriques Lentre analogiques x du convertisseur 1 Lentre analogiques x du convertisseur 2 Le buer dentre de la liaison srie x Le buer de sortie de la liaison srie x La vitesse (baudrate) de la liaison srie x Type 16 bits 16 bits 16 bits 16 bits ( !3 pf utiles) rel rel 16 bits 16 bits 16 bits Type C unsigned char, uchar unsigned char, uchar unsigned int, uint unsigned int, uint oat oat unsigned int, uint unsigned int, uint unsigned int, uint

Attention ! Ne confondez pas GPIOB_ODR, le registre de donnes en sortie du port GPIOB, interne au STM32, et PORTB, le VTREG indiquant les niveaux physiques des pattes du STM32, vues depuis le monde extrieur. Les deux entits (GPIOB_ODR et PORTB) existent bien mais peuvent prendre des valeurs direntes. Cela correspond en mode rel au cas o une broche dentre TTL est niveau haut alors que le bit du registre correspondant reste au niveau bas ( cause dune mauvaise conguration du GPIOB_CRH par exemple).

Fonctions prdnies En voici quelques unes parmi les plus courantes (liste complte et documentation de Vision -Debugging - Debug Functions - Predined Function). Ces fonctions lorsquelles sont insres dans une script de simulation (chier .ini) permettent de se fabriquer un scnario dvnements et de visualiser ce quil se passe en raction. Lide de base de toute cette approche est de tester un excutable sans modier une seule ligne de celui-ci : le code est instrumentalis depuis lextrieur. 31

Retour void int int void

Nom printf getint rand twatch

Argument ("format",...) ("chane dinvite") (int seed) (ulong states)

Description comme printf() du langage C-ANSI demande lutilisateur de fournir un entier fournit un entier 32768..+32767 alatoire sign -

bloque lexcution de la fonction durant le nombre de coups dhorloge pass en argument. excute depuis une fonction une commande au dbogueur lit un octet (char) en mmoire lit un mot (int) en mmoire crit un octet en mmoire crit un mot en mmoire

void uchar uchar void void

exec _RBYTE _RWORD _WBYTE _WWORD

("commande(chane)" (adresse) (adresse) (adresse, uchar valeur) (adresse, uchar valeur)

Remarque : mis part printf(), vous ne disposez pas des fonctions dentres/sorties (stdio) du langage C. Cela peut paratre limitatif mais noubliez pas que le but des ces fonctions est de faire de la mise au point dapplication et non des interfaces homme/machine volues.

Les fonctions de mise au point La syntaxe gnrale de ces fonctions est la suivante : FUNC <type> <nom> (<paramtres>) { <dclarations locales> <instructions> } Exemple : /* Etat() * Indique le niveau dentre appliqu AN0, et qqs registres. */ FUNC void Etat (void) { /* pas de variables locales dans cet exemple */ printf ("===================================\n") ; printf (" Entree analogique-0 : %f Volts \n", ADC1_IN1) ; /* VTREG ADC1_IN1 */ printf ("===================================\n\n") ; printf ("****** Registres du STM32 *******\n") ; 32

printf ("* R1 R2 R3 R4 R5 R6 \n") ; printf ("* %08X %08X %08X %08X %08X %08X \n", , R1, R2, R3, R4, R5, R6) ; /* registres */ printf ("********************************\n") ; printf ("* Registre (SP) : %04X\n", R13) ; printf ("* Program Counter : %06LX\n",R15 ) ; /* PC courant */ /* (lettre L) entier long (32 bits)*/ printf ("********************************\n") ; } Pour appeler cette fonction, il faut taper sur la ligne de commande du dbogueur :Etat() Etat() et lexcution, dans la fentre de messages du dbogueur, pourrait ressembler : =================================== Entree analogique-0 : 2.452000 Volts =================================== ****************** Registres du STM32 ******************* * R1 R2 R3 R4 R5 R6 * 00000001 00000200 40010C00 00000000 00000000 00000000 ******************************** * Registre (SP) : 20004FB8 * Program Counter : 80004A0 ********************************

La fonctions signal La syntaxe gnrale de ces fonctions est la suivantes : SIGNAL void <nom> (<arguments>) { <dclarations locales> <instructions> } Une fonction signal permet de rpter des actions externes, comme par exemple lenvoi de signaux priodiques ou dimpulsions vers le STM32 simul. Une fonction signal doit toujours faire appel twatch() ou swatch(). Voici un exemple de fonction qui envoie des rampes priodiques dchelons de tension (100 mV) vers lentre analogique n 0. 33

/* Analog0(oat Maxi) * Simule un signal variable analogique appliqu lentre AN0. * Argument dentre fournir : la valeur maxi du signal. * Forme du signal : priodique, fonction toit asymtrique, par incr* ments (ou dcrments) de 0,1V. */ Signal void Analog0 (oat Maxi) { oat SignAnalog ; printf ("Signal en marche. Maximum = %f.\n", Maxi) ; while (1) /* linni... Donc signal priodique */ { SignAnalog = 0.0 ; while (SignAnalog Maxi) /* Croissance du signal */ { ADC1_IN0 = SignAnalog ; /* Signal appliqu au VTREG ADC1_IN0 */ swatch (0.01) ; /* Attendre 0.01 secondes */ SignAnalog = SignAnalog + 0.1 ; /* Signal crot */ } SignAnalog = Maxi ; while (SignAnalog Maxi) /* Croissance linaire du signal */ { ADC1_IN0 = SignAnalog ; /* Signal appliqu au VTREG ADC1_IN0 */ swatch (0.02) ; /* Attendre 0.02 secondes */ SignAnalog = SignAnalog - 0.2 ; /* Signal crot */ } } } Dans cet exemple, vous notez lutilisation dune variable locale SignAnalog, du VTREG ADC1_IN0 des fonctions prdnies printf() et swatch(). Lintervalle entre deux paliers de tension conscutifs est de 4 secondes lors de la monte du signal, et de 2 secondes lors de la descente. La valeur maximale atteinte par le signal est donne en argument lors de lappel, par exemple Analog0(1.2) Une fois ainsi lance, cette fonction ne sarrte que si on dtruit la fonction par la commande au dbogueur : SIGNAL KILL Analog0 Restrictions et dirences par rapport au C-ANSI Lors de lcriture dune fonction, lutilisateur emploie un langage C limit selon les points essentiels suivants (vous trouverez plus de dtails p. 119 dans Vision2 Getting Started) : Majuscules et minuscules sont confondues : les VTREG ADC1_IN2 ou PORTC peuvent aussi tre dsign Adc1_In1 et portC. 34

Pas de prprocesseur : #define, #include, #ifdef ne marchent pas ! Dclaration de variables globales interdites. On peut cependant utiliser des grandeurs globales si on les a au pralable fabriques sous dbogueur par la commande DEFINE . Aucune variable ne peut tre initialise lors de sa dclaration. Structures interdites ! Le type de la valeur retourne par une fonction ne peut tre un pointeur. Pas de rcursivit. Pas dappel indirect de fonctions. Quelques commandes utiles En guise de bouquet nal, voici listes ci dessous quelques commandes qui pourront agrmenter lutilisation de ces fonctions utilisateurs : Nom EVALUATE KILL KILL SIGNAL DEFINE Aectation INCLUDE DIR 1 Exemple EVAL 0x1234 KILL FUNC * FUNC Toto SIGNAL KILL Titi SIGNAL STATES DEFINE CHAR UnOctet DEFINE FLOAT UnReel UnOctet=0x80 UnReel=3.14159 INCLUDE Tutu.ini DIR SIGNAL DIR UFUNC DIR BFUNC DIR FUNC Action donne la valeur de 0x1234 dans diverses bases de reprsentation dtruit toutes les fonctions (signal ou non) charges par lutilisateur dans le dbogueur dtruit la fonction de mise au point Toto() dtruit la fonction signal Titi() donne ltat des diverses fonctions signal dnit une grandeur globale intitule UnOctet dnit un rel global aecte un octet aecte un rel charge le dbogueur avec les fonctions ou les commandes incluses dans le chier Tutu.ini liste toutes les fonctions signal liste toutes les fonctions de mise au point cres par lutilisateur liste les fonctions prdnies liste toutes les fonctions de Vision

35

Vous aimerez peut-être aussi