30 1 3MB
UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
1
Sommaire 1. 2.
Introduction ........................................................................................................................ 4 Présentation des outils de developpement.......................................................................... 4 2.1. MPLAB IDE .............................................................................................................. 4 2.1.1. Rapide prise en main du logiciel ........................................................................ 5 2.1.1.1. Création du premier projet ......................................................................... 5 2.1.1.2. Simulation du premier projet...................................................................... 9 2.2. MPLAB ICD2 .......................................................................................................... 11 2.3. dsPICDEM2 ............................................................................................................. 11 2.4. Documentations utiles .............................................................................................. 12 3. Notion sur le projet et la programmation en C................................................................. 12 3.1. Composition du projet.............................................................................................. 12 3.2. Accès aux variables .................................................................................................. 13 3.3. Accès direct à un bit d’un registre de configuration ................................................ 14 3.4. Registres gérant les interruptions ............................................................................. 14 3.5. Déclaration d’une routine d’interruption.................................................................. 16 4. Application sous dsPIC30F.............................................................................................. 17 4.1. Gestion des interruptions externes INT0 et INT1 .................................................... 17 4.1.1. Avant propos .................................................................................................... 17 4.1.2. Programmation ................................................................................................. 17 4.1.2.1. Initialisation des interruptions INT0 et INT1........................................... 17 4.1.2.2. Déclaration du prototype de la fonction ................................................... 18 4.1.2.3. Ecriture des routines d’interruptions associées ........................................ 18 4.1.2.4. Ecriture de la fonction principale (main.c)............................................... 18 4.2. Gestion des Timers 1 et 3......................................................................................... 19 4.2.1. Avant Propos .................................................................................................... 19 4.2.2. Programmation ................................................................................................. 24 4.2.2.1. Initialisation des Timers ........................................................................... 24 4.2.2.1.1. Timer1 ................................................................................................... 24 4.2.2.1.2. Timer3 ................................................................................................... 25 4.2.2.2. Déclaration du prototype des fonctions.................................................... 25 4.2.2.3. Ecriture des routines d’interruptions associées ........................................ 26 4.2.2.3.1. Timer1 ................................................................................................... 26 4.2.2.3.2. Timer3 ................................................................................................... 27 4.2.2.4. Ecriture de la fonction principale (main.c)............................................... 27 4.2.3. Simulation ........................................................................................................ 28 4.3. Gestion de l’UART .................................................................................................. 28 4.3.1. Avant propos .................................................................................................... 28 4.3.2. Programmation ................................................................................................. 32 4.3.2.1. Initialisation de l’UART........................................................................... 32 4.3.2.2. Déclaration du prototype de la fonction ................................................... 32 4.3.2.3. Ecriture des routines d’interruptions associées ........................................ 33 4.3.2.4. Ecriture de la fonction principale (main.c)............................................... 34 4.3.3. Simulation ........................................................................................................ 36 4.4. Gestion de la PWM .................................................................................................. 37 4.4.1. Avant propos .................................................................................................... 37 4.4.2. Programmation ................................................................................................. 41 4.4.2.1. Initialisation du module PWM ................................................................. 41 UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
2
4.4.2.2. Déclaration du prototype de la fonction ................................................... 42 4.4.2.3. Ecriture de la fonction principale (main.c)............................................... 43 4.4.3. Simulation ........................................................................................................ 43 4.5. Gestion du convertisseur Analogique Numérique 10 bits........................................ 44 4.5.1. Avant propos .................................................................................................... 44 4.5.2. Programmation ................................................................................................. 53 4.5.2.1. Initialisation de l’ADC ............................................................................. 53 4.5.2.2. Déclaration du prototype de la fonction ................................................... 54 4.5.2.3. Ecriture des routines d’interruptions associées ........................................ 54 4.5.2.4. Ecriture de la fonction principale (main.c)............................................... 54 4.5.3. Simulation ........................................................................................................ 57 4.6. Gestion du SPI pour communication avec LCD ...................................................... 57 4.6.1. Avant propos .................................................................................................... 58 4.6.2. Programmation ................................................................................................. 60 4.6.2.1. Initialisation du module SPI..................................................................... 61 4.6.2.2. Sous-programme d’écriture sur l’afficheur LCD ..................................... 61 4.6.2.3. Déclaration du prototype des fonctions.................................................... 64 4.6.2.4. Ecriture de la fonction principale (main.c)............................................... 64 4.6.3. Simulation ........................................................................................................ 64 5. Conclusion........................................................................................................................ 64 6. Le noyau temps-réel PICOS............................................................................................. 65 6.1. Présentation .............................................................................................................. 65 6.1.1. Les avantages d’un noyau temps-réel .............................................................. 65 6.1.2. Les inconvénients d’un noyau temps-réel ........................................................ 65 6.2. PICOS : un noyau temps-réel, multitâches, préemptif, normalisé ........................... 66 6.2.1. Un noyau temps-réel. ....................................................................................... 66 6.2.2. Un noyau multitâches....................................................................................... 66 6.2.3. Un noyau préemptif.......................................................................................... 66 6.2.4. Un noyau normalisé ......................................................................................... 66 6.3. Mise en œuvre de PICOS ......................................................................................... 67 6.3.1. Objectif et attentes............................................................................................ 67 6.3.2. Etape n°1 : se procurer les outils nécessaires ................................................... 67 6.3.3. Etape n°2 : installer et configurer les outils ..................................................... 67 6.3.4. Etape n°3 : création et paramétrage du projet PICOS ...................................... 68 6.3.5. Etape n°4 : Ajouter les fichiers au projet ......................................................... 69 6.3.6. Etape n°5 : Paramétrer les chemins des ressources.......................................... 69 6.4. Comprendre le fonctionnement de PICOS, visite guidée du code ........................... 70 6.4.1. Le fichier main.c .............................................................................................. 70 6.4.2. Le fichier Tascdesc.c........................................................................................ 73 6.4.3. Le fichier tsk_taskX.c ...................................................................................... 76 6.5. Création d’une nouvelle tâche.................................................................................. 77 6.5.1. Création d’un nouveau fichier tsk_taskX.c ...................................................... 77 6.5.2. Modifications dans le fichier taskdesc.c .......................................................... 77 6.5.3. Modifications dans le fichier define.h.............................................................. 80 6.6. Exemple de fonction pilotée par PICOS : contrôle moteur par PWM ..................... 80 6.6.1. Phase de développement .................................................................................. 81 6.6.2. Intégration du code dans l’environnement de PICOS ...................................... 82 7. Conclusion........................................................................................................................ 83
UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
3
1. Introduction La plupart des processus industriels sont maintenant commandés et supervisés par des batteries de microcontrôleurs. Ces derniers permettent une meilleur flexibilité de commande des processus, de plus ils apportent des solutions de communication, qui pour lors ne sont pas réalisables dans un processus totalement analogique. Ainsi l’industrie tend de plus en plus vers des communications Machine to Machine (M2M) permettant de suivre la qualité de la fabrication et de corriger des dysfonctionnements rapidement. Pour exemple, si sur une machine-outil un roulement s’use plus rapidement que prévu, ou qu’une cote n’est plus exacte, les capteurs le détectent et envoient l’information à la machine en amont qui peut prendre la décision de changer sa commande. Tout ceci demande une intelligence embarquée des capteurs de plus en plus importante. Ainsi les capteurs standards dans le passé mesuraient une grandeur voulue, maintenant grâce aux microcontrôleurs embarqués, il est possible de compenser les grandeurs d’influences, de donner des informations de diagnostic permettant de faire remonter des informations de dysfonctionnement aux calculateurs. Une tendance actuelle est aussi de pouvoir paramétrer les capteurs à distance. A la lumière de ceci, nous pouvons en déduire que les microcontrôleurs au plus près du processus (gestion de capteur, de commande moteur,…) doivent gérer un flux de données conséquents tout en respectant des contraintes temps réel. L’unité de valeur TR57 a pour but de sensibiliser les étudiants à la commande temps réel des processus. Une commande est dite temps-réel lorsqu’elle respecte deux principes : Le temps d’acquisition des données : le traitement et la restitution de la commande respecte des temps définis dans le cahier des charges. La commande doit être déterministe, c'est-à-dire que pour une entrée donnée la sortie doit toujours être la même.
2. Présentation des outils de developpement 2.1. MPLAB IDE MPLAB IDE est un environnement intégré de développement permettant de programmer toutes les familles de microcontrôleur de Microchip. De base et en version gratuite disponible sur www.microchip.com , il est possible de développer des applications uniquement en langage Assembleur. L’adjonction de module tel que C18 ou C30 permet de programmer en C respectivement des microcontrôleurs de la famille 18F et 30F. Ces modules sont aussi disponibles sur le site internet mais seulement en version étudiante. Cette version optimise le code pendant une durée de 60jours puis fonctionne sans optimisation de code. L’option d’optimisation est inutile car l’utilisation ultérieur du noyau temps réel PICos(voir www.picos18.com) de la société PRAGMATEC n’utilise pas l’optimisation du code. La suite des explications est donnée pour la version v7.5 de MPLAB IDE et MPLAB C30 version 2.05. Les développements se font sur une cible dsPIC30F4011.
UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
4
2.1.1. Rapide prise en main du logiciel Cette rapide prise en main du logiciel n’a pas pour but d’expliquer toutes les fonctionnalités de MPLAB IDE et de MPLAB C30, mais d’expliquer le minimum pour pouvoir lancer un projet. Après avoir installé le logiciel MPLAB IDE et MPLAB C30, lancer l’application. 2.1.1.1.Création du premier projet La première étape est de vérifier que MPLAB C30 est bien installé. Pour ce faire cliquer sur ProjectèSet Language Tool Locations
Vérifier que le compilateur est bien installé. Puis faire OK
La création d’un nouveau projet se fait par ProjectèProject Wizard.. Cliquer sur suivant puis choisir la cible (dans notre cas dsPIC30F4011) puis Suivant.
Il faut maintenant choisir le langage de programmation, nous développons une application pour dsPIC30F il nous faut donc choisir Active Toolsuite : Microchip C30 Toolsuite. Puis cliquer sur Suivant
UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
5
Entrer le nom de votre projet ainsi que son emplacement sur le disque dur. Puis cliquer sur Suivant. Attention le nom de votre projet ne doit pas dépasser les 8 caractères et il est souvent préconisé de n’avoir aucun espace dans l’adresse de votre dossier ou le projet est sauvegardé.
Vous pouvez inclure des fichiers qui existent déjà dans votre dossier (fichier header *.h ou fichier contenant du code *.c, fichier linker .gld ou tout autre fichier reconnu par l’environnement de développement). Dans notre cas aucun fichier n’est à joindre. Cliquer sur Suivant, puis vérifier les données et cliquer sur Terminer si tout vous convient.
Votre projet est maintenant créé. Il faut toutefois encore y adjoindre quelques fichiers. Recherchez sur votre ordinateur le fichier p30f4011.gld, ce fichier permet de faire le lien lors de la compilation entre votre programme et le dsPIC utilisé. Il se peut que se fichier se trouve sous le répertoire C:\Program Files\Microchip\MPLAB ASM30 Suite\Support\gld En d’autres cas faire une recherche sous Windows. Lorsque vous avez trouvé ce fichier, le copier dans le dossier du projet. Recherchez aussi le fichier p30f4011.h, ce fichier contient tous les registres du microcontrôleur. Il se peut que ce fichier se trouve sous le répertoire Emplacement où est installé MPLAB C30\support\h En d’autres cas faire une recherche sous Windows.
Lorsque vous avez trouvé ce fichier, le copier dans le dossier du projet. Il vous suffit maintenant d’inclure ces deux fichiers dans le projet en cliquant (clique droit) sur votre projet puis Add Files UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
6
Choisir p30f4011.h puis cliquer sur Ouvrir Répéter l’opération mais en choisissant dans le menu déroulant Fichier de type è Linker Scripts (*.gld) sélectionner p30f4011.gld puis ouvrir. Il faut encore configurer certains bits pour que votre projet soit prêt. Cliquer sur ConfigureèConfiguration Bits Ces registres permettent de configurer les bits de fonctionnement du microcontrôleur. Oscillator Source = Primary Oscillator Cette configuration indique que le microcontrôleur utilise le quartz branché sur les broches OSC1/OSC2. Primary Oscillator Mode = XT w/PLL4x Le quartz branché sur les Pin OSC1 et OSC2 donne la fréquence de fonctionnement du CPU (Fcy=Fosc/4). Ce microcontrôleur peut augmenter la vitesse CPU en utilisant une PLL tout en gardant le même quartz. Ainsi si le mode XT w/PLL4x est sélectionné, la PLL augmente de 4 fois la fréquence de fonctionnement du CPU. Il est possible de l’augmenter de 8 fois ou même de 16 fois. Dans notre cas Fcy=Fosc/4=(Fquartz*4)/4=Fquartz La connaissance de Fcy est très importante lorsque vous configurerez des registres basés sur le temps (PWM, Timer, UART,..) Il existe d’autres entrées d’oscillateur. Le choix de XT est dû au fait que nous utilisons une horloge externe basée sur un quartz. Watchdog Timer=Disable Permet d’inhiber la fonction Watchdog Timer, car sinon votre microcontrôleur se réinitialise au bout d’un certain temps. La configuration donnée est :
Votre projet est prêt, il ne vous reste plus qu’à y ajouter le fichier comprenant la fonction principale. Créer un nouveau fichier en faisant Fichierè Add New File to Project… entrer le nom de votre fichier (dans notre cas main.c) puis enregistrer.
UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
7
Taper le code suivant : #include "p30F4011.h" int main (void) { while(1) ; } Puis compiler ctrl+F10 ou alors ProjectèBuild All. La compilation doit se faire sans erreur. Pour la simulation, nous pouvons remarquer que ce programme réalise une boucle infinie sans rien faire. Il est important de toujours mettre un while(1) à la fin de votre programme car sinon le compteur programme pourrait se perdre en engendrant un plantage du système. Modifions le programme pour réaliser des additions et comparaisons afin de pouvoir le simuler. #include "p30F4011.h" int valeur1,valeur2; //déclaration de deux entier signé unsigned int valeur3 ; //déclaration d’un entier non signé int stop ; //déclaration d’un entier permettant de savoir quand arrêter la boucle int main (void) { valeur1=0; //initialisation de valeur1 à 0 valeur2=0; //initialisation de valeur2 à 0 valeur3=0; //initialisation de valeur3 à 0 stop=0; //initialisation de stop à 0 while(stop==0) //tant que boucle stop est égale à 0 { valeur1++ ; //incrémentation de valeur1 valeur2+=valeur1+10 ; //valeur2=valeur2+valeur1+10 valeur3=valeur1+valeur2; //valeur3= valeur2+valeur1 if(valeur3>=1000) //Si valeur3 supérieur ou égale à 1000 alors on //arrête la boucle { stop=1; //boucle stop =1 } } while(1) ; } Lorsque vous avez tapé ce code compiler votre projet. (ctrl+F10). Passons maintenant à la simulation.
UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
8
2.1.1.2.Simulation du premier projet L’environnement permet de simuler et debugger votre programme sans utiliser de microcontrôleur. Les fonctionnalités explicitées sont celles de base, mais bien d’autres actions peuvent être réalisées par le simulateur. (Simulation de la liaison RS232, d’entrées-sorties,…). Configuration du simulateur Cliquer sur Debugger Select Tool MPLAB SIM
Lorsque vous recliquez sur Debugger, de nouvelles options s’affichent. Sélectionner DebuggerèSettings
Entrer la valeur du Quartz utilisé. La simulation ne prend pas en compte la PLL d’entrée, pour elle dans tous les cas Fcy=Processor Frequency/4 Dans notre cas, nous avons utilisé une PLLx4 Donc le Processor Frequency=Fquartz*4 A.N. : Fquartz=7,3728MHz Processor Frequency=7,3728*4=29,4912 MHz Cliquer sur Ok
Placer maintenant des breakpoints au niveau de stop=0; stop=1; while(1);. Pour placer des breakpoints cliquer dans la barre grise au niveau de la ligne où vous voulez que la simulation s’arrête.
Raccourcis clavier pour la simulation F6=Reset du processeur F7=mode pas à pas en entrant dans toutes les fonctions F8=mode pas à pas les fonctions sont simulées comme une unique instruction F9=lance la simulation jusqu’au prochain breakpoint.
UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
9
Avant de simuler il serait peut être intéressant de suivre l’évolution de certaines variables, pour ce faire cliquer sur ViewèWatch Une fenêtre s’affiche, dans Symbol Name, entrer les variables ou registres que vous voulez surveiller. Dans notre cas valeur1, valeur2, valeur3 et stop.
Lancer la simulation par F9. Elle va s’arrêter sur votre premier Breakpoint stop=0 ; Passer maintenant en mode Pas à Pas (F7) et suivre l’évolution des grandeurs. L’affichage en mode Hexadécimal des grandeurs d’évolution n’est peut être pas dans notre cas adéquat. Cliquer sur la grandeur d’évolution que vous souhaitez afficher dans une autre base puis Clique droitèProperties et dans le menu sélectionné Format=Decimal. L’affichage se fait maintenant en décimal. Apres avoir passé le programme en revue, il serait peut être intéressant de connaître le temps que met le microcontrôleur pour effectuer la première boucle while(stop==0) ;. Pour ce faire cliquer sur Debugger StopWatch. Ce module permet de connaître le temps parcouru entre deux breakpoints à condition de l’initialiser à chaque fois.
Enlever le breakpoint au niveau de la ligne stop=1 ; Reset du microcontrôleur par la touche F6. Puis Lancer la simulation par la touche F9. La simulation s’arrête au niveau de stop=0 ; Cliquer dans la fenêtre StopWatch sur Zero, ceci aura pour effet de remettre le compteur à 0.
UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
10
Appuyer sur F9, le programme s’arrête sur la boucle while(1). On peut lire sur la StopWatch 91,417101µs. Ainsi entre les deux breakpoints il s’est écoulé 91,417101µs
Cette fonctionnalité nous sera utile lorsque nous simulerons les interruptions des Timers. Ainsi nous pourrons rapidement vérifier la fréquence des interruptions et donc la fréquence du Timer. 2.2. MPLAB ICD2 MPLAB ICD2 est un système permettant de debugger et programmer les microcontrôleurs. Le débuggage se fait sur la cible même (In circuit Debugger). Le choix entre la programmation et le débuggage se fait sous MPLAB. Attention à ne pas sélectionner les deux fonctionnalités en même temps. Pour la programmation de la carte dsPICDEM2, sélectionner Programmerè Select ProgrammerèMPLAB ICD2. Puis ProgrammerèConnect Enfin ProgrammerèProgramm
2.3. dsPICDEM2 La carte dsPICDEM2 est une carte développement créée par Microchip. Cette carte permet de tester différents PIC 18 broches, 28 broches et 40 broches en boitier PDIP ou SPDIP. De base la carte comporte un dsPIC30F4011. Il est possible d’accéder à toutes les broches du microcontrôleur via des connecteurs, toutefois certaines fonctionnalités sont embarquées sur la carte afin d’avoir un mini environnement de développement. Ainsi la carte comprend un contrôleur RS232, un contrôleur CAN (Controller Area Network), des Leds de visualisation, des boutons, un potentiomètre, une sonde de température et un afficheur LCD (2*16 caractères). Un Connecteur ICD permet la programmation et le débuggage de cette carte par MPLAB ICD2 UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
11
2.4. Documentations utiles Avant de commencer la programmation pure de fonctionnalités, certains documents sont nécessaires : Sur internet (www.microchip.com) dsPIC30Family Reference Manual Documentation expliquant tous les registres du dsPic30FXXXX avec la fonction de chaque bit, ainsi que des exemples de fonctionnalités. Certaines fonctionnalités et certains registres sont détaillés mais non implémentés sur le 30F4011. 30F4011 datasheet Documentation expliquant tous les registres du dsPic30F4011. C30_Users_Guide Document expliquant la programmation en C avec le module C30 de Mplab. Attention des différences peuvent apparaître avec C18. C18 à été développé par Microchip alors que C30 a été sous-traité par Mircrochip à une société tierce. 16-BIT LANGUAGE TOOLS GETTING STARTED Manuel de prise en main du module C30. Sur le CD fournit avec le dsPICDEM2 dsPICDEM 2 Users Guide DS-51558A.pdf Document expliquant les fonctionnalités de la carte dsPICDEM2 ainsi que les méthodes d’accès à l’afficheur LCD et la configuration des cavaliers et switches pour vos applications.
3. Notion sur le projet et la programmation en C 3.1. Composition du projet Avant de commencer tous les petits exemples, il est important de comprendre la structure générale des projets. Un projet est composé de différents fichiers : main.c Ce fichier comprend le programme principal du projet. Code du fichier main.c #include "define.h" int main ( void ) {//code du projet} UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
12
define.h Ce fichier comprend toutes les #include importants du projet. Code du fichier define.h //fichier comprenant tous les registres du dsPIC30F4011 #include p30F4011.h //fichier comprenant tous les prototypes des fonctions utilisées #include "fonction.h" //Si nous utilisons la fonction printf avec l’UART nous devrons inclure stdio.h #include fonction.h Ce fichier comprend tous les prototypes des fonctions utilisées. Ainsi une fonction peut être appelée dans tous les fichiers incluant #include "define.h" Code du fichier define.h /********************************************************************** * Fonction prototypes. **********************************************************************/ nom_fonction.c Ce fichier comprend une fonction. Le fichier prendra le nom de la fonction. Si la fonction est l’initialisation d’un périphérique, la description de l’interruption du périphérique est aussi incluse dans le fichier. Tous les fichiers .c doivent comporter #include "define.h" afin d’avoir accès à toutes les fonctions. Créer dès le départ le fichier main.c fonction.h et define.h, ceci permettra de les inclure dans chaque mini projet qui suivra. 3.2. Accès aux variables Nous déclarons les variables globales dans le fichier main.c, pour avoir accès à ces variables dans un autre fichier que main.c, il faut déclarer dans le fichier les variables avec l’attribut extern. Attention à ne pas réinitialiser la variable dans le fichier sinon cela causera une erreur de compilation. Pour exemple : Création d’une variable entière dans main.c Déclaration dans le fichier main.c int tab=0 ; UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
13
Besoin de la variable tab dans le fichier modif_uart.c Déclaration de la variable dans modif_uart.c extern int tab ; 3.3. Accès direct à un bit d’un registre de configuration Tous les registres de configuration sont déclarés dans p30F4011.h. Lorsque vous voulez accéder à un bit du registre, il faut taper le nom du registre (NomRegistre) suivi par bits puis d’un point et du nom du bit (NomBit) que vous voulez modifier. La syntaxe est la suivante NomRegistrebits.NomBit Exemple pour le registre INTCON2 INTCON2 Bit
15
14
ALTIVT
DISI
13 12 11 10 9 8 7 6 5 -
-
-
-
- - - - -
4
3
2
1
0
INT4EP
INT3EP
INT2EP
INT1EP
INT0EP
L’accès au bit INT0P se fait de la façon suivante : INTCON2bits.INT0EP = 1 ; 3.4. Registres gérant les interruptions Les registres IEC0, IEC1 et IEC2 gèrent l’autorisation des interruptions 1 = Autorisation de l’interruption 0 = Inhibe l’interruption IEC0 Bit
15 CNIE Bit 7 T3IE
14 MI2CIE 6 T2IE
13 SI2CIE 5 OC2IE
12 NVMIE 4 IC2IE
11 ADIE 3 T1IE
10 U1TXIE 2 OC1IE
9 U1RXIE 1 IC1IE
8 SPI1IE 0 INT0IE
CNIE =Interruption sur changement d’état des entrées MI2CIE = Interruption du bus I2C lors d’une collision SI2CIE = Interruption lorsqu’un transfert I2C est fini NVMIE = Interruption lorsque l’écriture dans la mémoire Non Volatile est fini ADIE = Interruption lorsque l’ADC a fini de convertir une donnée U1TXIE = Interruption lorsque l’UART1 a transmis un caractère U1RXIE = Interruption lorsque l’UART1 a reçu un caractère SPI1IE = Interruption du module SPI1 T3IE = Interruption du Timer3 T2IE = Interruption du Timer2 OC2IE = Interruption de l’Output Compare Channel 2 IC2IE = Interruption de l’Input Compare Channel 2 T1IE = Interruption du Timer1 OC1IE = Interruption de l’Output Compare Channel 1 IC1IE = Interruption de l’Input Compare Channel 1 INT0IE = Interruption de la broche INT0
UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
14
IEC1 Bit Bit
15 7 INT2IE
14 6 T5IE
13 5 T4IE
12 4 OC4IE
11 C1IE 3 OC3IE
10 2 IC8IE
9 U2TXIE 1 IC7IE
8 U2RXIE 0 INT1IE
C1IE = Interruption du Bus CAN U2TXIE = Interruption lorsque l’UART2 a transmis un caractère U2RXIE = Interruption lorsque l’UART2 a reçu un caractère INT2IE = Interruption de la broche INT2 T5IE = Interruption du Timer5 T4IE = Interruption du Timer4 OC4IE = Interruption de l’Output Compare Channel 4 OC3IE = Interruption de l’Output Compare Channel 3 IC8IE = Interruption de l’Input Compare Channel 8 IC7IE = Interruption de l’Input Compare Channel 7 INT1IE = Interruption de la broche INT1 IEC2 Bit Bit
15 7 PWMIE
14 6 -
13 5 -
12 4 -
11 FLTAIE 3 -
10 2 -
9 1 -
8 QEIIE 0 -
FLTAIE = Interruption de FAULTA dans le module PWM QEIIE = Interruption du module de Quadrature QEI PWMIE interruption du module PWM lorsqu’une période est accomplie Les registres IFS0, IFS1 et IFS2 indiquent les interruptions qui sont actives IFS0 Bit Bit
15 CNIF 7 T3IF
14 13 12 MI2CIF SI2CIE NVMIF 6 5 4 T2IF OC2IF IC2IF
11 ADIF 3 T1IF
10 9 U1TXIF U1RXIF 2 1 OC1IF IC1IF
8 SPI1IF 0 INT0IF
IFS1 Bit Bit
15 7 INT2IF
14 6 T5IF
13 5 T4IF
12 11 C1IF 4 3 OC4IF OC3IF
10 2 IC8IF
9 U2TXIF 1 IC7IF
8 U2RXIF 0 INT1IF
10 2 -
9 1 -
8 QFIIF 0 -
IFS2 Bit
15 Bit 7 PWMIF
14 6 -
13 5 -
12 4 -
11 FLTAIF 3 -
Lorsqu’une interruption survient, il faudra effacer le bit correspondant à l’interruption dans la routine.
UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
15
Exemple : Interruption activée sur le Timer2 (IEC0bits.T2IE=1) dans la routine d’interruption il faudra penser à mettre IFS0bits.T2IF=0 3.5. Déclaration d’une routine d’interruption Il y a trois façons de déclarer une interruption 1. Aucune sauvegarde de contexte void __attribute__((__interrupt__)) _Nom de l’interruption(void) {//code de l’interruption} 2. Sauvegarde de contexte rapide par push. et pop.s Sauvegarde des bits DC N OV Z C et des registres W0 à W3 du CPU DC = Demi Carry permet de savoir si un carry est survenu sur les 4 bits de poids faible d’un octet (8bits) ou bien si un carry est survenu sur les 8bits d’un mot (16bit). N = Signe du registre de l’Unité Arithmétique et Logique OV = Overflow de l’Unité Arithmétique et Logique Z = Zero de l’Unité Arithmétique et Logique C = Carry de l’Unité Arithmétique et Logique void __attribute__((__interrupt__, __shadow__)) _Nom de l’interruption (void) {//code de l’interruption} 3. Sauvegarde du contexte en spécifiant les données à sauvegarder et restaurer. Les variables 1 et 2 seront sauvegardées sur la pile puis restituées à la fin de l’interruption void __attribute__((__interrupt__(__save__(variable1,variable2)))) _ Nom de l’interruption (void) {//code de l’interruption} Tous les noms d’interruption sont déclarés dans le fichier p30F4011.gld. Attention il faut enlever un underscore (_) par rapport au nom donné dans le fichier Quelques exemples des interruptions les plus usitées : Interruption ADC Timer1 INT0 INT1 U1TX U1RX
Déclaration en C _ADCInterrupt _T1Interrupt _INT0Interrupt _INT1Interrupt _U1TXInterrupt _U1RXInterrupt
UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
16
4. Application sous dsPIC30F 4.1. Gestion des interruptions externes INT0 et INT1 4.1.1. Avant propos Le but de cette manipulation est d’utiliser les interruptions INT0 et INT1. Ces deux interruptions sont des interruptions externes qui interviennent lors d’un changement d’état sur la broche RE8 (INT0 pin 17) et RD0 (INT1 pin 23).
INTCON2 Bit
15
14
ALTIVT
DISI
13 12 11 10 9 8 7 6 5 -
-
-
-
- - - - -
4
3
2
1
0
INT4EP
INT3EP
INT2EP
INT1EP
INT0EP
ALTIVT: Autorise la table des interruptions alternatives 1 = Use alternate vector table 0 = Use standard (default) vector table DISI: Statut des instructions INTxEP: Choix du front de détection 1 = Interruption sur front descendant 0 = Interruption sur front montant 4.1.2. Programmation L’exemple que nous allons développer sur le dsPICDEM2 utilise le front montant sur INT0 pour déclencher une interruption qui éteindra la LED du port RB0, le front descendant sur INT1 déclenche une interruption qui allumera la LED du port RB0. Créer un nouveau projet en incluant tous les fichiers de base d’un projet (define.h, fonction.h, main.c, p30f4011.h et p30f4011.gld). Tout d’abord afin de respecter la composition d’un projet, créons un fichier init_int01.c. Ce fichier comprendra l’initialisation des interruptions INT0 et INT1 ainsi que les programmes d’interruption. 4.1.2.1.Initialisation des interruptions INT0 et INT1 Nous voulons la détection du front montant sur INT0 et du front descendant sur INT1 INTCON2bits.INT0EP = 1; INTCON2bits.INT1EP = 0; Ensuite il faut effacer le flag d’interruption correspondant à l’interruption extérieure 0 et 1 IFS0bits.INT0IF = 0; IFS1bits.INT1IF = 0; UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
17
Puis activer les interruptions IEC0bits.INT0IE = 1; IEC1bits.INT1IE = 1; void init_INT01(void) { INTCON2bits.INT0EP = 1; //interruption sur Front descendant INTCON2bits.INT1EP = 0; //interruption sur Front montant IFS0bits.INT0IF = 0; //Efface le flag d'interruption INT0 IEC0bits.INT0IE = 1; //Autorise les interruptions INT0 IFS1bits.INT1IF = 0; //Efface le flag d'interruption INT1 IEC1bits.INT1IE = 1; //Autorise les interruptions INT1 } 4.1.2.2.Déclaration du prototype de la fonction Penser à mettre le prototype de la fonction dans fonction.h afin que tous les fichiers associés au projet aient accès à cette fonction. Dans fonction.h void init_INT01(void) ; 4.1.2.3.Ecriture des routines d’interruptions associées Les interruptions n’ont pas besoin de sauvegarder le contexte car aucun calcul n’est effectué et donc nous n’utilisons pas l’ALU Le bouton branché sur la broche RE8 (INT0 pin 17) éteint la Led branchée sur le PORTB0 void __attribute__((__interrupt__)) _INT0Interrupt(void) { LATBbits.LATB0 = 0; //Eteint la LED IFS0bits.INT0IF = 0; //efface le flag d’interruption INT0 } Le bouton branché sur la broche RD0 (INT1 pin 23) allume la Led branchée sur le PORTB0 void __attribute__((__interrupt__)) _INT1Interrupt(void) { LATBbits.LATB0 =1; // Eteint la LED IFS1bits.INT1IF = 0; //efface le flag d’interruption INT1 } Penser toujours à effacer les flags d’interruptions à la fin de chaque routine d’interruption 4.1.2.4.Ecriture de la fonction principale (main.c) Dans la fonction principale, il faudra initialiser les interruptions avec le sous-programme que nous avons écrit. Penser aussi à définir le PORTB0 en sortie.
UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
18
#include "define.h" int main ( void ) { init_INT01(); TRISBbits.TRISB0=0; //configure le PORTB0 comme sortie LATBbits.LATB0=0; //initialise le PORTB0 à 0 while(1); } Compiler le projet (ctrl+F10) puis programmer la platine dsPICDEM2. En appuyant sur les boutons poussoir INT0 et INT1 juste au dessus de l’afficheur LCD, vous verrez une Led s’allumer ou s’éteindre selon vos actions. 4.2. Gestion des Timers 1 et 3 4.2.1. Avant Propos Le but de cette manipulation est de pouvoir initialiser des Timers pour réaliser diverses actions relatives au temps. La famille des dsPIC30F possède différents types de Timers (A,B,C) qui caractérisent leurs fonctionnalités. Type A Les Timers de type A (Timer1 la plus part du temps) peuvent opérer en mode « 32kHz Low Power » et peuvent utiliser une source externe comme référence de comptage. La plupart du temps ce type de Timer est utilisé dans les OS temps réel pour décompter le temps.
UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
19
Registre contrôlant le Timer1 T1CON Bit Bit
15 TON 7 -
14 6 TGATE
13 TSIDL 5 TCKPS1
12 4 TCKPS0
11 3 -
10 2 TSYNC
9 1 TCS
8 0 -
TON: Lance le Timer 1 = Lance TIMER1 0 = Arrête TIMER1 TSIDL: Mode de fonctionnement en IDLE mode 1 = Arrête le TIMER1 en Idle Mode 0 = Continue les opérations de TIMER en IDLE mode TGATE: Le TIMER1 est mis en mode "Timer Gated Time Accumulation" en d'autres termes le compteur du TIMER1(TMR1) s'incrémente uniquement lorsque TxCK est à l'état 1. 1 = Autorise le mode "Timer gated time accumulation" (TCS doit être mis à ‘0’ quand TGATE = 1) 0 = Inhibe la fonction "Timer gated time accumulation" (Lecture d'un ‘0’ si TCS = 1) TCKPS: Prescaler de l'horloge TIMER1 11 = 1:256 prescaler 10 = 1:64 prescaler 01 = 1:8 prescaler 00 = 1:1 prescaler TSYNC: Synchronisation avec l'horloge extérieure Quand TCS = 1: 1 = Synchronisation avec l'horloge externe 0 = Pas de Synchronisation avec l'horloge externe Quand TCS = 0: Ce bit est ignoré TCS: Choix de la source du TIMER1 1 = Horloge de la PIN TxCK 0 = Horloge interne (FOSC/4) Type B Les Timers de type B (Timer2 et Timer4 si implémenté) sont capables d’être concaténés avec un Timer de type C. Le bit T32 du registre TxCON contrôle alors le compteur 32bits. L’horloge d’incrémentation du Timer de type B utilise un prescaler permettant de fonctionner à une autre vitesse que celle du CPU (Tcy).
UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
20
Registre contrôlant le Timer2 ou Timer4 T2CON et T4CON Bit Bit
15 TON 7 -
14 6 TGATE
13 TSIDL 5 TCKPS1
12 4 TCKPS0
11 3 T32
10 2 -
9 1 TCS
8 0 -
TON: Lance le Timer Si T32=1 (Mode Timer 32 bits) 1 = Lance TIMER2 ou 4 32bits 0 = Arrête TIMER2 ou 4 32bits Si T32=0 (Mode Timer 16 bits) 1 = Lance TIMER2 ou 4 16bits 0 = Arrête TIMER2 ou 4 16bits TSIDL: Mode de fonctionnement en IDLE mode 1 = Arrête le TIMER1 en Idle Mode 0 = Continue les opérations de TIMER en IDLE mode TGATE: Le TIMER2 ou 4 est mis en mode "Timer Gated Time Accumulation" en d'autres termes le compteur du TIMER2 ou 4 (TMR2 ou 4) s'incrémente uniquement lorsque TxCK est à l'état 1. 1 = Autorise le mode "Timer gated time accumulation" (TCS doit être mis à ‘0’ quand TGATE = 1) 0 = Inhibe la fonction "Timer gated time accumulation" (Lecture d'un ‘0’ si TCS = 1) TCKPS: Prescaler de l'horloge TIMER2 ou 4 11 = 1:256 prescaler UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
21
10 = 1:64 prescaler 01 = 1:8 prescaler 00 = 1:1 prescaler T32 : Sélectionne le Mode Timer 32bits 1 = Le Timer 2/3 ou 4/5 forme un timer 32bits 0 = Timer2 ou 4 16bits TCS: Choix de la source du TIMER2 ou 4 1 = Horloge de la PIN TxCK 0 = Horloge interne (FOSC/4) Type C Les Timers de type C (Timer3 et Timer5 si implémenté) ont la possibilité d’être concaténés avec un Timer de Type B. Un des Timers de type C (Timer3 en général) permet de lancer une conversion Analogique Numérique. (Le choix de la source de déclenchement des conversions ADC se fait dans les registres de l’ADC « SSRC de ADCON1 »)
UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
22
Registre contrôlant le Timer3 ou 5 T3CON Bit Bit
15 TON 7 -
14 6 TGATE
13 TSIDL 5 TCKPS1
12 4 TCKPS0
11 3 -
10 2 -
9 1 TCS
8 0 -
TON: Lance le Timer 1 = Lance TIMER3 ou 5 0 = Arrête TIMER3 ou 5 TSIDL: Mode de fonctionnement en IDLE mode 1 = Arrête le TIMER1 en Idle Mode 0 = Continue les opérations de TIMER en IDLE mode TGATE: Le TIMER3 ou 5 est mis en mode "Timer Gated Time Accumulation" en d'autres termes le compteur du TIMER3 ou 5 (TMR3 ou 5) s'incrémente uniquement lorsque TxCK est à l'état 1. 1 = Autorise le mode "Timer gated time accumulation" (TCS doit être mis à ‘0’ quand TGATE = 1) 0 = Inhibe la fonction "Timer gated time accumulation" (Lecture d'un ‘0’ si TCS = 1) TCKPS: Prescaler de l'horloge TIMER3 ou 5 11 = 1:256 prescaler 10 = 1:64 prescaler 01 = 1:8 prescaler 00 = 1:1 prescaler TCS: Choix de la source du TIMER3 ou 5 1 = Horloge de la PIN TxCK 0 = Horloge interne (FOSC/4)
Chaque TIMERx (x=1, 2, 3, 4, 5) possède les registres suivant : TMRx = registre contenant la valeur de comptage. PRx = registre contenant la période du Timer. Ce registre est comparé à TMRx lorsque les deux registres sont égaux le Timer se réinitialise. La valeur de PRx se calcul de la façon suivante : Time _ x =
(PRx * Pr escaler )
Fcy Time _ x * Fcy PRx = Pr escaler
UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
23
4.2.2. Programmation Créez un nouveau projet en incluant tous les fichiers de base d’un projet (define.h, fonction.h, main.c, p30f4011.h et p30f4011.gld). Tout d’abord afin de respecter la composition d’un projet, créons un fichier init_int01.c. Ce fichier comprendra l’initialisation des interruptions INT0 et INT1 ainsi que les programmes d’interruption. 4.2.2.1.Initialisation des Timers Le timer1 sera utilisé pour gérer une horloge, l’interruption permettra de mettre à jour les variables heure, minute, seconde et milliseconde. La fréquence d’interruption sera donc la milliseconde. Le timer3 permettra de créer la fréquence d’échantillonnage du convertisseur Analogique Numérique. Nous utiliserons une fréquence d’échantillonnage de 2kHz 4.2.2.1.1.
Timer1
Créer tout d’abord un fichier inclus dans le projet appelé init_timer1.c. Après avoir inclus le fichier define.h (#include "define.h"), commencer l’initialisation du TIMER1.
Créer un sous programme void init_timer1(void) Avec la description du registre donnée plus haut suivre les étapes suivantes : 1. Calculer la valeur de PR1 pour savoir si un prescaler est utile. (Time_x=1ms, Fcy= (Fosc/4)*PLL4x=Fosc) Pr escaler = 1 Time _ x * Fcy PR1 = = 1*10 −3 * 7,3728 *10 6 = [7372,8] = 7372 = 0 x1CCC < 0 xFFFF Pr escaler PR1 ne dépasse pas 65536 donc le prescaler n’a pas d’utilité. 2. Arrêter le Timer1 3. Configurer le TIMER1 : Le Timer1 peut continuer les opérations en IDLE mode. La fonction "Timer gated time accumulation" n’est pas utilisée. Le prescaler vaut 1. La synchronisation avec l’horloge externe n’est pas utilisée. La source du timer est le quartz. Initialiser la valeur du Registre contenant la valeur du compteur à 0. Initialiser le Registre contenant la période du Timer
UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
24
Registres configurés : T1CON=0X00; TMR1=0; PR1=7372; Nous voulons utiliser l’interruption du Timer1 pour mettre à jour notre horloge Effacer le Flag d'interruption du Timer1 IFS0bits.T1IF = 0; Puis autoriser les interruptions du Timer1 IEC0bits.T1IE = 1 ; La configuration du Timer1 est faite et il peut être lancé. T1CONbits.TON=1 ;
4.2.2.1.2. Timer3 La configuration est la même pour le Timer3 à la différence près que le temps est calculé pour avoir une fréquence d’échantillonnage de 2kHz. La configuration se fera dans un nouveau fichier appelé init_timer3.c. L’interruption du Timer3 n’est pas utile lorsque nous utilisons ce Timer pour lancer la conversion ADC. Toutefois, afin de vérifier la fréquence des interruptions en simulation, nous pouvons autoriser l’interruption.
(Time_x=0,5ms, Fcy= (Fosc/4)*PLL4x=Fosc) Pr escaler = 1 Time _ x * Fcy = 0,5 *10 −3 * 7,3728 *10 6 = [3686,4] = 3686 = 0 x0 E 66 < 0 xFFFF PR3 = Pr escaler
4.2.2.2.Déclaration du prototype des fonctions Penser à mettre le prototype de la fonction dans fonction.h afin que tous les fichiers associés au projet aient accès à cette fonction. Dans fonction.h void init_timer1(void) ; void init_timer3(void) ;
UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
25
4.2.2.3.Ecriture des routines d’interruptions associées 4.2.2.3.1. Timer1 L’interruption du Timer1 permettra de mettre à jour l’horloge du système, pour ce faire il faut incrémenter le compteur de milliseconde (msec) puis traiter les cas de figure selon que les msec soient supérieurs à 1000, que les secondes (sec) soient supérieures à 60, que les minutes (min) soient supérieure s à 60 et que les heures (heure) soient supérieure s à 24. Cette interruption utilise des opérations d’addition et de comparaisons, c’est pour ces raisons qu’il faut sauvegarder le contexte de l’unité Arithmétique et Logique avant d’entrer dans la routine d’interruption (déclaration de l’interruption avec l’attribut __shadow__) Nous profiterons aussi de cette interruption pour faire changer l’état de la LED RB0 toutes les secondes. Il faut penser à ré-autoriser les interruptions sur Timer1 à la fin de la routine. La routine d’interruption du Timer1 (init_timer1.c) void __attribute__((__interrupt__, __shadow__)) _T1Interrupt(void) { msec++; if (msec >= 1000) { msec = 0; sec++; LATBbits.LATB0 =~ LATBbits.LATB0; if(sec>=60) { sec=0; min++; if (min >= 60) { min = 0; heure++; if(heure>=24) { heure=0; } //fin if(heure>=24) }//fin if (min >= 60) }//fin if(sec>=60) }//fin if (msec >= 1000) IFS0bits.T1IF = 0; //ré-autorisation des interruptions } Vous avez pu remarquer que l’interruption a besoin des variables msec, sec, min, heure. Ces variables étant globales au projet elle seront déclarées dans le fichier main.c. Il faut juste penser à les déclarer dans le fichier init_timer1.c en tant que variables externes.
UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
26
4.2.2.3.2. Timer3 L’interruption du Timer3 n’est pas utile lorsque nous utilisons ce Timer pour lancer la conversion ADC. Toutefois afin de vérifier la fréquence des interruptions en simulation, nous pouvons écrire la routine d’interruption. Cette routine ré-autorise la prochaine interruption du Timer3. La routine d’interruption du Timer3 (init_timer3.c) void __attribute__((__interrupt__)) _T3Interrupt(void) { IFS0bits.T3IF = 0; //ré-autorisation des interruptions } 4.2.2.4.Ecriture de la fonction principale (main.c) Dans le fichier main.c, il faudra déclarer msec, sec, min, heure comme variable globale de type entier non signé. unsigned int msec, sec, min, heure ; La fonction principal main.c initialisera msec, sec, min, heure puis le port B0 comme port de sortie et enfin fera appel aux initialisations du Timer1 et Timer3. fichier main.c #include "define.h" unsigned int msec, sec, min, heure ; int main ( void ) { msec=0; sec=0; min=0; heure=0; LATBbits.LATB0 = 0; init_timer1(); init_timer3(); while(1); } Compiler tout le projet pour la simulation (ctrl+F10)
UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
27
4.2.3. Simulation Nous allons simuler le projet pour voir si les interruptions interviennent dans les temps impartis. Mettre un breakpoint dans l’interruption du Timer3, puis avec le module StopWatch vérifier que l’interruption intervient tous les 500µs. Enlever le breakpoint au niveau de l’interruption du timer3 et ajouter un breakpoint au niveau de msec++ dans l’interruption du timer1, vérifier à nouveau que l’interruption intervient toutes les 1ms. Vous pouvez aussi visualiser les variations des variables msec, sec, min, heure en appuyant sur F7 4.3. Gestion de l’UART Le but de cette manipulation est de pouvoir réaliser une communication entre le microcontrôleur et des périphériques extérieurs (ordinateur, modem,…) via une liaison série RS232. Dans le répertoire (mplabide) joint au tutoriel, un projet gère l’affichage d’un menu et la saisie de données (dossier « menu »). Dans ce projet vous trouverez des fonctions permettant de transformer un nombre saisi au clavier en entier interprétable par le microcontrôleur (int buffer2nombre(void)). Le nombre saisi doit être entouré de deux balises 0x0D (retour chariot) (0x0D 0x31 0x32 0x33 0x0D = 123) Ce projet contient aussi une fonction permettant de transformer un entier compris entre 0 et 10 en une chaine de caractères (void i2a(int valeur, unsigned char *tab)). Ces fonctions ont été développées pour une application bien spécifique, à vous de les modifier à votre guise. 4.3.1. Avant propos Le dsPIC30F comprend un UART Full Duplex de 8 ou 9bits. Les spécifications techniques sont : Gestion de la parité Paire, Impair, Pas de Parité (transfert de donnée 8bits uniquement) 1 ou 2 stop bits Générateur de Baud intégré Vitesse de transmission variant entre 29bps à 1,875 Mbps à une Fréquence Cycle Fcy de 30 MHz Les données reçues sont stockées dans un buffer First-In-First-Out (FIFO) d’une profondeur de 4 octets Les données à émettre sont stockées dans un buffer First-In-First-Out (FIFO) d’une profondeur de 4 octets La détection des erreurs de parité, de fin de trame (stop bit manquant) et de débordement des buffers sont intégrées au module. Supporte la transmission de donnée 9 bits avec détection du mode adresse (9ème bit=1) UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
28
Interruption de transmission et de réception Possibilité de connecter la broche de transmission sur la broche de réception pour diagnostic
Registre du module UART U1MODE Bit Bit
15 UARTEN 7 WAKE
14 6 LPBACK
13 USIDL 5 ABAUD
12 11 10 9 ALTIO 4 3 2 1 - PDSEL
8 0 STSEL
UARTEN: Activation de l'UART 1 = UART est actif. Les pins RX et TX sont contrôlées par l'UART 0 = UART est inactif. Les pins RX et TX sont des I/O standard USIDL: Opération en mode repos 1 = Arrêt des opérations en mode sommeil (SLEEP) 0 = Poursuite des opérations en mode sommeil (SLEEP) ALTIO: Alternance des transmissions - réceptions 1 = l'UART utilise UxATX et UxARX alternativement en réception puis en émission 0 = l'UART utilise les pins UxTX and UxRX respectivement pour l'émission et pour la réception Attention cette fonctionnalité n'est pas disponible sur tous les PICs WAKE: Action lorsqu'un Start bit est détecté en mode sommeil 1 = Réveil du µC 0 = Ne rien faire LPBACK: La broche TX est branchée à la broche RX en interne 1 = activation du mode 0 = désactivation du mode ABAUD: Activation de l'auto-détection de la vitesse de transfert en paramétrant l'unité de capture. L'unité doit capturer les fronts montant et descendant du bit de Start 1 = Input to Capture module from UxRX pin 0 = Input to Capture module from ICx pin PDSEL:Choix de la parité et du nombre de données transmises 11 = 9-bit data, pas de parité 10 = 8-bit data, parité pair 01 = 8-bit data, parité impair 00 = 8-bit data, pas de parité STSEL: Choix du nombre de bits de stop 1 = 2 Stop bits 0 = 1 Stop bit
UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
29
U1STA Bit
15 14 13 UTXISEL Bit 7 6 5 URXISEL ADDEN
12 4 RIDLE
11 UTXBRK 3 PERR
10 9 UTXEN UTXBF 2 1 FERR OERR
8 TRMT 0 URXDA
UTXISEL: Mode d'interruption sur transmission de caractère 1 = Une interruption intervient lorsqu'il y a un transfert du buffer de transmission au Transmit Shift register, par ce biais le buffer de transmission est vide. 0 = Une interruption est réalisée lorsqu'un caractère est transmis au Transmit Shift register, ce qui implique qu'au moins un caractère peut être ajouté dans le buffer de transmission UTXBRK: Transmission d'un break 1 = Transmission d'un état '0' pendant un minimum de 13 cycle d'horloge 0 = La broche Tx fonctionne normalement UTXEN: Autorisation des transmissions 1 = La pin UxTX est commandée par l'UART normalement 0 = Arrêt des transmissions, toutes les transmissions en cours sont arrêtées et les buffers sont réinitialisés. La broche Tx est contrôlée par la PORT. UTXBF: Statut du buffer de Transmission (Lecture seul) 1 = Le buffer est plein 0 = Le buffer n'est pas plein, un minimum d'une donnée peut être écrit dans le buffer TRMT: Statut du Transmit Shift Register (Lecture seul) 1 = Le Transmit shift register est vide et le buffer de transmission est vide 0 = Le Transmit shift register n'est pas vide, une transmission est en cours URXISEL: Mode d'interruption sur réception 11 = L'interruption intervient lorsque le buffer de réception est plein (4 données reçues) 10 = L'interruption intervient lorsque le buffer de réception est aux 3/4plein (3données reçues) 0x = L'interruption intervient lorsqu'un caractère est reçu ADDEN: Détection du mode Adresse 1 = Le mode adresse est activé. Si la transmission de 9 caractères n'est pas sélectionnée ce bit n'a pas d'effet. 0 = Le mode d'adresse est inhibé RIDLE: Réception en mode Idle (au repos) (Lecture seul) 1 = Le récepteur est en mode Idle 0 = Des données sont entrain d'être reçus PERR: Flag Erreur de parité (Lecture seul) 1 = Une erreur de parité a été détectée sur le caractère courant 0 = Aucune erreur de parité n'a été détectée UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
30
FERR: Flag erreur de forme non détection du bit de stop (Lecture seul) 1 = Une erreur de forme a été détectée sur le caractère courant 0 = Aucune erreur de forme n'a été détectée OERR: Flag erreur sur le buffer de réception (Lecture/effacement seul) Levé dans 3 cas: 1Le buffer de réception est plein 2Le Shift register de réception est plein et ne peut pas transférer les données au buffer de réception 3Le bit de stop du caractère dans UxRSR est détecté et indique que le caractère de UxRSR doit être transféré dans le buffer de réception 1 = Le buffer de réception a débordé 0 = Le buffer de réception n'a pas débordé URXDA: Des données sont disponibles dans le buffer de réception (Lecture seule) 1 = Des données sont disponibles dans le buffer de réception 0 = Le buffer de réception est vide La vitesse de transmission se calcule de la façon suivante Vitesse de transmission =
Fcy 16(UxBRG + 1)
Soit UxBRG =
Fcy −1 16(Vitesse de transmission)
A.N. pour une vitesse de transmission de 9600bauds (Fcy= (Fosc/4)*PLL4x=Fosc) 7 372 800 000 − 1 = [47] = 47 16(9600) Vitesse de transmission réelle calculée UxBRG =
7 372 800 000 = 9600bauds 16(47 + 1) Erreur due à l’approximation Vitesse de transmission =
Erreur% =
Vitesse de transmission voulu - Vitesse de Transmission réelle 9600 - 9600 = = 0% Vitesse de transmission voulu 9600
UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
31
4.3.2. Programmation 4.3.2.1.Initialisation de l’UART Créer tout d’abord un fichier inclus dans le projet appelé init_uart.c. Après avoir inclus le fichier define.h (#include "define.h"), commencer l’initialisation de l’UART.
Créer un sous-programme void init_uart(void) Suivre les étapes suivantes : 1. 2. 3. 4. 5. 6. 7. 8.
Arrêter l’UART Configurer l’UART (8bits, parité pair, 1bit de stop) Une interruption intervient quand le buffer de transmission est vide Choisir la vitesse de transmission (9600bauds) Effacer les flags d’interruption en émission et réception Activer les interruptions en émission et réception Activer l’UART Activer la broche Tx
void init_uart(void) { U1MODE=0x0003; U1STA=0x8000; U1BRG=47; //Efface le flag d'interruption en réception IFS0bits.U1RXIF=0; //Efface le flag d'interruption en émission IFS0bits.U1TXIF=0; //Autorisation des interruptions en réception IEC0bits.U1RXIE=1; //Autorisation des interruptions en émission IEC0bits.U1TXIE=1; // Activation de l'UART U1MODEbits.UARTEN=1; // Autorisation des transmissions U1STAbits.UTXEN=1; }
4.3.2.2.Déclaration du prototype de la fonction Penser à mettre le prototype de la fonction dans fonction.h afin que tous les fichiers associés au projet aient accès à cette fonction. Dans fonction.h void init_uart(void); UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
32
4.3.2.3.Ecriture des routines d’interruptions associées Pour l’émission de caractère nous utiliserons la fonction printf. Cette dernière fait partie de la librairie de fonction compris dans et gère automatiquement l’envoi de caractères à travers l’UART L’interruption de transmission de l’UART sert uniquement à remettre le flag d’interruption à zéro. void __attribute__((__interrupt__)) _U1TXInterrupt(void) { IFS0bits.U1TXIF=0; }
L’interruption de réception de caractère permet de transférer les données reçus dans un buffer circulaire. Ce buffer circulaire sera par la suite traité dans différentes parties du programme. Cette interruption permet aussi de savoir s’il y a une erreur sur le bit reçu (erreur de parité, de forme ou si le buffer de réception est plein).
Voir l’algorithme suivant
. UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
33
void __attribute__((__interrupt__, __shadow__)) _U1RXInterrupt(void) { if(nb_val>TAILLE_BUF) { erreur_RX.buffer_plein=1; } else { while(U1STAbits.URXDA==1)//tant qu'il y a des valeurs dans le buffer de réception on les stocke { if(U1STAbits.PERR==1)erreur_RX.parite=1;//test la parité if(U1STAbits.FERR==1)erreur_RX.forme=1;//test si bit de stop présent if(U1STAbits.OERR==1)erreur_RX.buffer_RXplein=1;//test si le buffer de réception est plein if(erreur_RX.parite==0&erreur_RX.forme==0&erreur_RX.buffer_RXplein==0) { bufferRX[ptrW++]=U1RXREG; if(ptrW>=TAILLE_BUF) { ptrW=0; } nb_val++; } else { erreur_RX.reception=1; } } } IFS0bits.U1RXIF=0; } 4.3.2.4.Ecriture de la fonction principale (main.c)
Le programme principal attend indéfiniment si un caractère est reçu. Dès qu’un caractère est reçu, on renvoie sa valeur par le port série, ceci permet de faire une fonction d’écho. Avant d’écrire la fonction main il faut déclarer certaines variables utiles. La première est la définition de la taille du buffer de réception, puis il faut créer une structure traçant toutes les erreurs survenues pendant la phase de réception de caractère, par la suite il faut déclarer le buffer de réception puis les pointeurs (ptrW et ptrR) permettant de manipuler le buffer de réception et finalement le compteur nb_val permettant de savoir combien de valeurs sont présentes dans le buffer de réception. Dans la fonction main, nous initialiserons les variables nb_val, ptrW et ptrR à zéro puis ferons appel à la fonction d’initialisation de l’UART. UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
34
Le reste du programme principal est une boucle infinie qui affiche les caractères du buffer de réception. #include "define.h" #define TAILLE_BUF 20 struct erreurRX_type { unsigned char parite; unsigned char forme; unsigned char buffer_RXplein; unsigned char buffer_plein; unsigned char reception; } erreur_RX; unsigned char bufferRX[TAILLE_BUF]; int ptrW,ptrR,nb_val;
int main ( void ) { //remise à zéros du compteur du nombre de valeur dans le buffer de réception nb_val=0; //remise à zéro du pointeur d'écriture dans le buffer de réception ptrW=0; //remise à zéro du pointeur de lecture dans le buffer de réception ptrR=0; init_uart(); printf("fonction d'echo\n\r"); while(1) { while(nb_val!=0) { nb_val--; printf("caractere=%c\n\r",bufferRX[ptrR++]); if(ptrR>=TAILLE_BUF) { ptrR=0; } } } } L’utilisation de la fonction printf nécessite de changer une option de compilation, pour ce faire cliquer sur ProjectèBuild option èProject. Dans l’onglet MPLAB LINK 30, ajouter 512 à la valeur de HEAP SIZE puis cliquer sur OK Compiler le projet (ctrl+F10) UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
35
4.3.3. Simulation MPLAB IDE ne gère pas très bien la simulation de l’UART. Toutefois un module existe. Créer tout d’abord un fichier texte dans vote répertoire de projet appelé entree_uart.txt. Ce fichier comprendra toutes les entrées que vous voulez soumettre à votre UART. Attention ne taper que des chiffres représentant des nombres hexadécimaux. Pour exemple si vous tapez 0F, lors de la simulation le caractère reçu aura pour valeur 0x0F. Utilisez la table ASCII pour connaître les combinaisons hexadécimales des caractères que vous voulez envoyer.
Pour lors le fichier entree_uart.txt comprend les valeurs suivantes : 30 31 32 33 34 35 36 37 38 39 Choisir le simulateur MPLAB SIM, puis cliquer Debugger èSettings. Dans l’onglet UART IO cliquer sur Enable UART1 IO. Cliquer sur Browse et entrer le chemin du fichier entree_uart.txt. Rewind Input permet de lire en boucle le fichier texte. Dans Output cliquer sur Window. Un afficheur sera disponible en cliquant ViewèOutput, onglet SIM UART1. Sinon cliquer sur file et donner un nom de fichier recueillant les sorties de l’UART.
UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
36
Maintenant que vous avez initialisé la liaison série, vous pouvez lancer le programme et passer aux phases de vérification avec des breakpoints. Mettre des breakpoints dans les interruptions pour vérifier l’envoi de caractères ainsi que le remplissage du buffer de réception. Si vous ne mettez aucun breakpoint vous devriez voir dans la fenêtre SIM UART1 le résultat suivant. Ce programme peut aussi être testé sur la carte dsPICDEM2. Attention il faut mettre deux cavaliers sur H3 MALL et enlever les cavaliers sur H4 MALL. Utiliser un câble droit et configurer l’Hyperterminal avec la configuration que vous avez donnée à la liaison soit dans notre cas 8bits, parité paire, 1bit de stop, 9600bauds. La carte doit afficher les caractères que vous tapez au clavier.
4.4. Gestion de la PWM 4.4.1. Avant propos Les modules PWM sont utilisés dans tout ce qui est commande de moteur. (Moteur triphasé, Moteur à courant continu, Moteur DC Brushless,…). Les principales caractéristiques du module PWM : Chaque PWM possède 2 sorties PWMxL/PWMxH Les sorties peuvent être complémentaires ou indépendantes Il est possible de gérer des temps morts entre les broches PWMxL et PWMxH La polarité des broches PWM peut être programmée Différents mode de fonctionnement du compteur PWM Mode compteur Mode compteur décompteur Mode compteur décompteur avec la possibilité de rafraichir rafraîchir le rapport cyclique à la moitié de la période Mode compteur mono coup Programmation d’événements pouvant synchroniser une conversion ADC
UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
37
Les registres du module PWM PTCON Bit Bit
15 PTEN 7
14 13 PTSIDL 6 5 PTOPS
12 11 10 9 8 - 4 3 2 1 0 PTCKPS PTMOD
PTEN: arrête la base de temps de la PWM 1 = La base de temps est activée 0 = La base de temps est désactivée
PTSIDL: Etat de fonctionnement du module PWM en Idle Mode 1 = La base de temps du module est arrêtée en Idle Mode 0 = La base de temps du module fonctionne en Idle Mode PTOPS: A chaque fin de cycle de comptage une interruption peut être déclenchée. Ce registre permet de définir le nombre de cycles de comptage avant que l'interruption ne soit déclenchée. 1111 = 1:16 Postscale • • 0001 = 1:2 Postscale 0000 = 1:1 Postscale PTCKPS: Prescaler d'entrée de la base de temps de la PWM 11 = PWM time base input clock period is 64 TCY (1:64 prescale) 10 = PWM time base input clock period is 16 TCY (1:16 prescale) 01 = PWM time base input clock period is 4 TCY (1:4 prescale) 00 = PWM time base input clock period is TCY (1:1 prescale) PTMOD: Mode de fonctionnement de la PWM 11 = La base de temps fonctionne en mode compteur-décompteur continu. Une interruption est générée à la fin du cycle et au milieu du cycle permettant de réaliser une mise à jour du registre PDCx (=duty cycle) 10 = La base de temps fonctionne en mode compteur-décompteur continu. Une interruption est générée à la fin de cycle permettant de réaliser une mise à jour du registre PDCx (=duty cycle) voir aussi le registre PTOPS 01 = La base de temps fonctionne uniquement sur un cycle. A la fin du cycle une interruption est générée et le bit PTEN passe à 0 00 = La base de temps fonctionne en mode compteur continu. Une interruption est générée à la fin de cycle permettant de réaliser une mise a jour du registre PDCx (=duty cycle) voir aussi le registre PTOPS
UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
38
PWMCON2 Bit Bit
15 7 -
14 6 -
13 5 -
12 4 -
11
10
3 -
2 IUE
9 SEVOPS 1 OSYNC
8 0 UDIS
< SEVOPS> Post Scaler sur les Evénements PWM (signal de lancement ADC) = 1111 1:16 Postcal ... = 0001 1:2 Postcal = 0000 1:1 Postcal IUE = 1 rafraîchissement du Duty cycle (PDC) immédiatement = 0 rafraîchissement du Duty cycle (PDC) sur chaque période PWM OSYNC = 1 écriture sur les sorties par OVDCON synchronisé par le cycle PWM = 0 écriture sur les sorties par OVDCON au prochain cycle machine Tcy UDIS = 1 Le rafraîchissement du temps de cycle (PDC) and du buffer (PTPER) est inhibé = 0 Le rafraîchissement du temps de cycle (PDC) and du buffer (PTPER) est possible DTCON1 Bit Bit
15 14 7 6 DTAPS
13 5
12 4
11 3 DTA
10 2
9 1
8 0
DTAPS Prescaler Value pour DTB 11 : Prescaler de 8Tcy 10 : Prescaler de 4Tcy 01 : Prescaler de 2Tcy 00 : Prescaler de Tcy DTA Dead Time A DTA=(DeadTime*Fcy/(Prescaler_Value) Exemple 1µs DTA=(1e-6*7 372 800 000)/1=[7,3728]=0x7=00111(b) Les temps-morts sont automatiquement mis en marche lorsque les sorties PWM sont commandées en mode complémentaire pour des raisons de commutation des circuits électroniques.
UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
39
Explication des différents modes Mode comptage continu :
Mode comptage continu mono coup :
UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
40
Mode comptage décomptage continu :
PTPER est le registre gérant le temps de cycle de la PWM En mode comptage continu et comptage mono coup : Fcy PTPER = −1 Fpwm * PTMR _ prescaler En mode comptage décomptage continu : PTPER =
Fcy −1 2 * Fpwm * PTMR _ prescaler
Les registres PDCx contiennent la valeur du Duty cycle. En mode comptage continu :
PTPER − PDCx PTPER Attention en mode comptage-décomptage continu si PDCx=PTPER cela donne un Duty Cycle de 50% et non de 100%. 2 * PTPER − PDCx Duty _ cycle% = PTPER Ceci n’est pas spécifié dans la documentation Microchip Duty _ cycle% =
4.4.2. Programmation 4.4.2.1.Initialisation du module PWM Créer tout d’abord un fichier inclus dans le projet appelé init_pwm.c. Après avoir inclus le fichier define.h (#include "define.h"), commencer l’initialisation de l’UART. UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
41
Créer un sous programme void init_pwm(void) Suivre les étapes suivantes : 1. Arrêter le compteur PWM 2. Configurer la PWM en mode comptage décomptage continu. Une interruption par cycle 3. La fréquence de la PWM est de 20 kHz 4. Les 3 paires de sortie PWM sont utilisées en mode complémentaire 5. Le rafraichissement du Duty cycle (PDC) se fait sur chaque période PWM 6. Le rafraichissement du temps de cycle (PDC) et du buffer (PTPER) est possible 7. Le Dead Time est de 1µs 8. PWM1H/L Duty cycle de 2/3 9. PWM1H/L Duty cycle de 1/2 10. PWM1H/L Duty cycle de 1/3 11. Lancer la PWM #include "define.h" void init_pwm(void) { PTCON=0x0002; PTPER=183; PWMCON1=0x00FF; PWMCON2=0x0000; /* DTA=(DeadTime*Fcy/(Prescaler_Value) Exemple 1µs DTA=(1e-6*7 372 800 000)/1=[7,3728]=0x7=00111(b) */ DTCON1=0x0007; //initialisation du temps de cycle PDC1=244; PDC2=183; PDC3=122; //lancement de la PWM par PTEN PTCONbits.PTEN=1; }
4.4.2.2.Déclaration du prototype de la fonction Penser à mettre le prototype de la fonction dans fonction.h afin que tous les fichiers associés au projet aient accès à cette fonction. Dans fonction.h void init_pwm(void);
UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
42
4.4.2.3.Ecriture de la fonction principale (main.c) La fonction principale appelle juste la fonction init_pwm. #include "define.h" int main ( void ) { init_pwm(); while(1); } Compiler le projet (ctrl+F10) 4.4.3. Simulation Sous MPLAB il est possible de tracer l’état de certaines sorties au fil des temps cycle. Pour avoir accès à cette fonction, sélectionner DebuggerèMPLAB SIM, puis dans Settings… Cocher les cases Trace All et si vous voulez que la simulation s’arrête quand le buffer de trace est plein cocher Break on Trace Buffer Full. Cliquer sur OK.
Pour sélectionner les sorties à tracer, cliquer sur ViewèSimulator Logic Analyzer Une nouvelle fenêtre s’ouvre cliquer alors sur Channel pour sélectionner les voies que vous voulez tracer. Dans notre cas PWM1L/H, PWM2L/H et PWM3L/H. Cliquer sur OK
Lancer la simulation et visualiser les différentes PWM.
UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
43
4.5. Gestion du convertisseur Analogique Numérique 10 bits Dans bon nombre d’applications de régulation, il est primordial de mesurer des grandeurs analogiques. Cette partie permet d’assimiler le principe de fonctionnement du module ADC 10bits du dSP30Fxxx. 4.5.1. Avant propos Les spécifications techniques du convertisseur ADC sont : Conversion Analogique Numérique par conversion successive (SAR) Jusqu'à 1M Sample par seconde 16 voies de conversion Possibilité d’utiliser une référence de tension externe 4 canaux permettant des conversions différentielles Possibilité de sampler 4 valeurs simultanément. Choix de la source de déclenchement des conversions (Timer 3/ PWM/…) Choix du mode de représentation des conversions Buffer 16 mots contenant les valeurs des différentes voies converties Possibilité de convertir le Buffer 16 mots en deux Buffers 8 mots Choix de fonctionnement en Idle mode et Sleep mode
UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
44
Le schéma interne du Convertisseur ADC 10bits, permet de mieux comprendre son fonctionnement. Nous remarquons qu’un seul convertisseur est implanté dans le module, les données converties transiteront toutes par des module appelés S/H (Sample and Hold) associés à un canal (CH0, CH1, CH2, CH3). Ces modules prennent en compte la valeur de la tension analogique provenant des broches ANx connectées à leur borne pendant un temps appelé Sample time et bloquent cette valeur jusqu'à la conversion. Les canaux branchent la sortie du module S/H au convertisseur. Ainsi il est possible de prendre en compte 4 entrées simultanément. Chaque entrée sera convertie tour à tour. Le canal 0 est quant à lui relié à toutes les entrées ANx, ceci permet de faire une conversion séquentielle de chaque entrée reliée au canal 0. UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
45
Vous pouvez maintenant comprendre que deux modes sont possibles, le mode Simultané et le mode Séquentiel.
0 RGH6 LP XO W DQp
6 DP SO HB&+
6 DP SO HB&+
$1
$1
6 DP SO HB&+ $1
6 DP SO HB&+
$1
2 UGHGH FRQYHUVLRQ
$1
$1
$1
$1
0 RGHVpTXHQW LHO
6 DP SO HB&+
2 UGHGH FRQYHUVLRQ
$1
$1
$1
$1
$1
$1
Le temps de conversion (« partie sujette à révision ») Le temps de conversion du module peut se choisir selon la configuration des bits ADC du registre ADCON3. Tad représente un temps d’horloge du module, pour réaliser une conversion il faut 12Tad en respectant un Tad supérieur à 83,33ns (Pour 1MSps) Tcy ( ADCS + 1) TAD = 2 2T ADCS = AD − 1 Tcy
UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
46
Le schéma d’entrée d’une voie du convertisseur est représenté ci-dessous :
La capacité Chold se charge à la valeur de la tension d’entrée et garde cette valeur jusqu’à la conversion. Le temps de sampling doit être assez long pour que la capacité se charge à la tension d’entrée. Dans la documentation un minimum de 12Tad est recommandé pour le temps de sampling en mode Simultané.
UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
47
Exemple : Il faut avoir un temps Tad> 84ns Fcy=Fosc*PLL4x/4=Fosc Fcy=7,3728Mhz => Tcy135ns Si on configure 15 Tad pour le temps de sampling et une période de 4Tcy pour l'horloge interne du module, cela nous donne Tad = X*Tcy = 4*Tcy => ADCS=2X-1=7 15 Tad = Tsmpl La période de conversion est de 12 Tad Si nous convertissons 2 voies en simultané Temps total d'acquisition= Temps de sampling + (Nombre de channels) (Temps de conversion) Temps total d'acquisition= 15 Tad +2*12 Tad= (15+2*12)4*Tcy=156 Tcy =Tta Fosc=7,3728MHz Tta=156 Tcy=156*1/Fosc=21µs Soit Fta=47261Hz Pour le calcul des temps de conversion cela peut différer selon les modes disponible, référezvous au dsPIC30F Family Réference Les registres importants du convertisseur ADC. ADCON1 Bit Bit
15 14 13 ADON ADSIDL 7 6 5 SSRC
12 4 -
11 3 SIMSAM
10 2 ASAM
9 8 FORM 1 0 SAMP DONE
ADON: Mise en marche du convertisseur 1 = A/D enclenche le convertisseur 0 = A/D arrête le convertisseur ADSIDL: Mode de fonctionnement en mode IDLE 1 = arrête la conversion en mode IDLE 0 = les conversions continuent en mode IDLE FORM: Format de sortie des conversions 11 = Bit de signe + 9 bits de conversion + 6bits à 0 (DOUT = sddd dddd dd00 0000) 10 = 10 bits de conversion + 7bits à 0 (DOUT = dddd dddd dd00 0000) 01 = Entier signé 7bits de signe 9 bits de conversion (DOUT = ssss sssd dddd dddd) 00 = Entier non signé 6bits de signe 10 bits de conversion (DOUT = 0000 00dd dddd dddd) SSRC: Source lançant la conversion (SAMP=1) 111 = Le compteur interne arrête le sampling et lance la conversion (auto conversion) 110 = Reservé UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
48
101 = Reservé 100 = Reservé 011 = Un cycle PWM (voir la configuration des événements SEVOPS du registre PWMCON2) PWM) arrête le sampling et lance la conversion (auto conversion) 010 = Le Timer3 arrête le sampling et lance la conversion (auto conversion) 001 = Une transition active sur la broche INT0 arrête le sampling et lance la conversion (auto conversion) 000 = L'effacement du bit SAMP arrête le sampling et lance la conversion (auto conversion) SIMSAM: Sélectionne le mode de multisampling (utilisable si CHPS = 01 ou 1x) 1 = Le sampling de CH0, CH1, CH2, CH3 se fait simultanément (quand CHPS = 1x) ou le sampling de CH0 et CH1 se fait simultanément (quand CHPS = 01) 0 = Le sampling se fait de façon séquentielle avec CH0 ASAM: Mode sampling automatique de l'ADC 1 = Le sampling commence directement après la dernière conversion. Le bit SAMP bit est automatiquement mis à 1 0 = Le sampling commence quand le bit SAMP passe à 1 SAMP: A/D Sample Enable bit 1 = Au moins une valeur est en cours de sampling 0 = La ou les valeur(s) sont maintenues Quand ASAM = 0, écrire ‘1’ démarre la phase de sampling Quand SSRC = 000, écrire ‘0’ arrête la phase de sampling et lance la conversion DONE: A/D Etat de la conversion 1 = A/D la conversion est réalisée 0 = A/D la conversion est en cours Effacé par logiciel ou par le début d'une nouvelle conversion. L'effacement de ce bit n'affecte aucune opération en cours.
UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
49
ADCON2 Bit Bit
15 14 13 VCFG 7 6 5 BUFS -
12 4
11 10 CSCNA 3 2 SMPI
9 8 CHPS 1 0 BUFM ALTS
VCFG: Configuration de la tension de référence VCFG A/D VREFH A/D VREFL 000 AVDD AVSS 001 External Vref+pin AVSS 010 AVDD External Vref-pin 011 External Vref+pin External Vref-pin 1xx AVDD AVSS CSCNA: Lorsque l'on fait de la conversion analogique numérique séquentielle de plusieurs entrées sur CH0, il faut mettre le bit CSCNA à 1 puis configurer le registre ADCSSL permettant de savoir quelles entrées sont prises en compte La conversion se fera toujours de AN0 à AN15, il n'est pas possible de choisir l'ordre 1 = Scan inputs 0 = Do not scan inputs CHPS: Sélectionne les canaux utilisés 1x = Conversion des canaux CH0, CH1, CH2 and CH3 01 = Conversion des canaux CH0 and CH1 00 = Conversion du canaux CH0 Quand SIMSAM bit (ADCON1) = 0 Le sampling des canaux se fait de façon séquentielle Quand SIMSAM bit (ADCON1) = 1 Le sampling multiple se fait d'après CHPS BUFS: Donne les buffers accessibles par l'utilisateur en mode double buffer voir (BUFM=1) 1 = l'ADC est en train de remplir les buffers 0x8-0xF, l'utilisateur doit récupérer les données des buffers 0x0-0x7 0 = l'ADC est en train de remplir les buffers 0x0-0x7, l'utilisateur doit récupérer les données des buffers 0x8-0xF SMPI: Indique quand une interruption doit survenir 1111 = Une interruption est réalisée à la fin des 16 séquences de sampling/conversion 1110 = Une interruption est réalisée à la fin des 15 séquences de sampling/conversion ..... 0001 = Une interruption est réalisée à la fin des 2 séquences de sampling/conversion 0000 = Une interruption est réalisée à la fin de chaque séquence de sampling/conversion BUFM: Configuration du BUFFER 1 = Le Buffer est configuré comme deux buffers de 8 mots ADCBUF(15...8), ADCBUF(7...0) 0 = Le Buffer est configuré comme deux buffers de 16 mots ADCBUF(15...0.)
UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
50
0 ALTS: Bit permettant de configurer les entrées des canaux. Il est possible de les modifier alternativement. 1 = Utiliser alternativement la valeur de MUXA puis la valeur de MUXB pour les entrées 0 = Toujours utiliser la valeur de MUXA pour les entrées ADCON3 Bit Bit
15 7 ADRC
14 6 -
13 5 -
12 4
11 10 SAMC 3 2 ADCS
9
8
1
0
SAMC: Temps pour l'auto sampling 11111 = 31 TAD ··· 00001 = 1 TAD 00000 = 0 TAD (autorisé si on réalise une conversion séquentielle utilisant plus d'un canal) ADRC: Choix de la source de temps du convertisseur ADC 1 = L'ADC utilise une horloge RC interne 0 = L'ADC utilise l'horloge système ADCS: Choix du temps de l'horloge du module ADC Tad 111111 = TCY/2 • (ADCS + 1) = 32 • TCY … 000001 = TCY/2 • (ADCS + 1) = TCY 000000 = TCY/2 • (ADCS + 1) = TCY/2 Pour Tad=X*Tcy Tcy ( ADCS + 1) = X * Tcy 2 ADCS = 2 X − 1
Tad =
ADCHS Bit Bit
15 14 CH123NB 7 6 CH123NA
13 CH123SB 5 CH123SA
12 CH0NB 4 CH0NA
11 10 9 8 CH0SB 3 2 1 0 CH0SA
CH123NB: Entrée négative du Canal 1, 2, 3 pour la configuration MUX B Même configuration de choix que pour CH123NA CH123SB: Entrée positive Canal 1, 2, 3 pour la configuration MUX B Même configuration de choix que pour CH123SA CH0NB: Entrée négative du Canal 0 pour la configuration MUX B Même configuration de choix que pour CH0NA CH0SB: Canal 0 Positive Input Select for MUX B Multiplexer Setting bits Même configuration de choix que pour 3-0 CH0SA UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
51
CH123NA: Entrée négative du Canal 1, 2, 3 pour la configuration MUX A 11 = Entrée négative CH1 est AN9, Entrée négative CH2 est AN10, Entrée négative CH3 est AN11 10 = Entrée négative CH1 est AN6, Entrée négative CH2 est AN7, Entrée négative CH3 est AN8 0x = Entrée négative CH1, CH2, CH3 est VREFCH123SA: Entrée positive Canal 1, 2, 3 pour la configuration MUX A 1 = Entrée positive CH1 est AN3, Entrée positive CH2 est AN4, Entrée positive CH3 est AN5 0 = Entrée positive CH1 est AN0, Entrée positive CH2 est AN1, Entrée positive CH3 est AN2 CH0NA: Choix de l’entrée négative du Canal 0 pour la configuration MUX A 1 = L'Entrée négative Channel 0 est AN1 0 = L'Entrée négative Channel 0 est VREFCH0SA: Choix de l’entrée positive du Canal 0 pour la configuration MUX A 1111 = L'entrée positive du Channel 0 est AN15 1110 = L'entrée positive du Channel 0 est AN14 1101 = L'entrée positive du Channel 0 est AN13 || || || 0001 = L'entrée positive du Channel 0 est AN1 0000 = L'entrée positive du Channel 0 est AN0
ADPCFG Bit
15 14 13 12 11 10 9 8 PCFG15 PCFG14 PCFG13 PCFG12 PCFG11 PCFG10 PCFG9 PCFG8 Bit 7 6 5 4 3 2 1 0 PCFG7 PCFG6 PCFG5 PCFG4 PCFG3 PCFG2 PCFG1 PCFG0
PCFGx 1= Entrée sortie numérique, dans l'ADC l'équivalent de la broche est relié à AVss 0= Entrée analogique, dans l'ADC la broche est reliée au canal souhaité ADCSSL Bit
15 14 13 12 11 10 9 8 CSSL15 CSSL14 CSSL13 CSSL12 CSSL11 CSSL10 CSSL9 CSSL8 Bit 7 6 5 4 3 2 1 0 CSSL7 CSSL6 CSSL5 CSSL4 CSSL3 CSSL2 CSSL1 CSSL0
Utilisé pour choisir les entrées à numériser lorsque l’on est en mode séquentiel (SIMSAM=0 bit (ADCON1)) CSSLx 1=L'entrée ANx est numérisée 0=L'entrée ANx n'est pas numérisée
UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
52
4.5.2. Programmation 4.5.2.1.Initialisation de l’ADC Créer tout d’abord un fichier inclus dans le projet appelé init_adc.c. Après avoir inclus le fichier define.h (#include "define.h"), commencer l’initialisation de l’ADC.
Créer un sous programme void init_ADC(void) Avec la description du registre donné plus haut suivre les étapes suivantes : 1. Arrêter l’ADC 2. Configurer l’ADC: Réaliser une conversion cyclique simultanée de AN2 et AN3 Le timer 3 lance périodiquement les conversions. Faire une conversion des canaux 0 et 1 Une interruption intervient à chaque fois que l’on refait un Utiliser le buffer en 2 buffers de 8bits Le temps de sampling est de 15Tad Tad=4Tcy Effacer les flags d’interruption Autoriser les interruptions Lancer le convertisseur numérique analogique void init_ADC(void) { ADCON1bits.SSRC=2; //réalise une conversion cyclique automatique de AN2 ET AN3 avec le Timer3 ADCON1bits.SIMSAM=1; //sample au même moment AN2 et AN3 ADCON1bits.ASAM=1; //mise à 1 de SAMP automatiquement après chaque conversion ADCON2bits.CHPS=1; //conversion du channel 0 et 1 (des canaux 0 et 1) ADCON2bits.SMPI=0; //une interruption est réalisée à chaque fois que l'on refait un sampling ADCON2bits.BUFM=1; //utilise le buffer en 2 buffers de 8bits ADCON3bits.SAMC = 15; ADCON3bits.ADCS = 7; ADCHSbits.CH123NA=0; //relie toutes les entrées négatives des channels 1/2/3 à VREFADCHSbits.CH123SA=1; //relie l'entrée positive CH1 à AN3 ADCHSbits.CH0NA=0; //relie l'entrée négative du channels 0 à VREFADCHSbits.CH0SA=2; //relie l'entrée positive du channels 0 à AN2 ADPCFGbits.PCFG2=0; //Définir l'entrée AN2 comme entrée analogique ADPCFGbits.PCFG3=0; //Définir l'entrée AN3 comme entrée analogique //efface le flag des interruptions ADC IFS0bits.ADIF = 0; //autorise les interruptions sur ADC IEC0bits.ADIE = 1; //Lance le convertisseur numérique analogique ADCON1bits.ADON = 1; } UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
53
4.5.2.2.Déclaration du prototype de la fonction Penser à mettre le prototype de la fonction dans fonction.h afin que tous les fichiers associés au projet aient accès à cette fonction. Dans fonction.h void init_ADC(void) ;
4.5.2.3.Ecriture des routines d’interruptions associées La routine d’interruption enregistre les données converties puis efface le flag associé à l’interruption. Il faudra déclarer en variable globale int Potentiometre et TempSensor. Nous utilisons le mode double buffer, il faudra donc faire attention à lire les données dans le bon buffer void __attribute__((__interrupt__)) _ADCInterrupt(void) { if(ADCON2bits.BUFS==1) { Potentiometer = ADCBUF0; TempSensor = ADCBUF1; } else { Potentiometer = ADCBUF8; TempSensor = ADCBUF9; } IFS0bits.ADIF = 0; } 4.5.2.4.Ecriture de la fonction principale (main.c) Dans la fonction main, nous afficherons en continu la valeur du potentiomètre et la valeur de la température par la liaison RS232 dans hyperterminal. Pour ce faire il faudra initialiser la liaison série avec tous les paramètres étudiés auparavant. Pour convertir les données de l’ADC en données affichables il faut créer une fonction de conversion (« adc2tabchar » fichier adc2tabchar.c). La conversion Analogique numérique est sur 10 bits et la plage d'évolution de la tension analogique d'entrée est de 5V soit une résolution de 5/(2^10-1)=0,004888V pour la variation d'un LSB Pour 1V après conversion 1/0,004888V soit 204,6 = 204 =0xCC Pour 0,1V après conversion 0,1/0,004888V soit 20,46=20= 0x14 Pour 0,01V après conversion 0,01/0,004888V soit 2,046=2= 0x2 Il suffit de comparer le nombre que nous voulons convertir en caractère par rapport à ces trois seuils. On réalise une boucle tant que le nombre est supérieur à 2. Dans cette boucle tant que le nombre est supérieur au seuil de 204 alors on incrémente les unités de 1 et on soustrait 204
UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
54
au nombre. La même méthode est utilisée pour incrémenter les dixièmes et les centièmes avec les seuils respectifs de 20 et 2. /*Fonction convertissant le résultat de l'ADC en une suite de caractères affichables sur écran. Cette fonction à été écrite pour Vref-=0 et Vref+=5V. le tableau passé en paramètre récupère les données de la façon suivante : Tab[0]=entier Tab[1]=dixième Tab[3]=centième */ void adc2tabchar(unsigned int resultatConv,char *Tab) { unsigned char unite = 0; unsigned char dixieme = 0; unsigned char centieme = 0;
//reset values
/* Conversion Analogique numérique sur 10 bits, La plage d'évolution de la tension analogique d'entrée est de 5V soit une résolution de Résolution= 5/(2^10-1)=0,004888V pour la variation d'un LSB Pour 1V après conversion 1/0,004888V soit 204,6 = 204 =0xCC Pour 0,1V après conversion 0,1/0,004888V soit 20,46=20= 0x14 Pour 0,01V après conversion 0,01/0,004888V soit 2,046=2= 0x2 */ while (resultatConv > 2) { if(resultatConv > 204) //test si supérieur à 1Volt { unite++; //incrémente les unités resultatConv -= 204; //soustraction d’1 Volt au nombre } else if(resultatConv > 20 && resultatConv 2 && resultatConv =750) //le curseur est positionné à la deuxième ligne { cpt_msec=msec-750; } else { cpt_msec=msec+250; } while(msec!=cpt_msec); } while(*affiche!=0x00) //tant que le pointeur sur le tableau ne rencontre pas la fin de la chaine de caractère { while(SPI1STATbits.SPITBF != 0); //tant que le Buffer d'émission est plein SPI1BUF = *affiche++; if(msec>=750) { cpt_msec=msec-750; } else { cpt_msec=msec+250; } while(msec!=cpt_msec); }}
UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
63
4.6.2.3.Déclaration du prototype des fonctions Penser à mettre le prototype de la fonction dans fonction.h afin que tous les fichiers associés au projet aient accès à cette fonction. Dans fonction.h void init_timer1(void); void init_SPI(void); void affiche_lcd(int ligne,unsigned char *affiche);void init_timer1(void) ; 4.6.2.4.Ecriture de la fonction principale (main.c) La programme principal lancera l’affichage du tableau premier_affiche, puis ne fera plus rien. #include "define.h" int msec,sec,min,heure,affiche; unsigned char premier_affiche[]="UTBM POWERED"; int main ( void ) { init_timer1(); init_SPI(); while(sec!=2); //attente de 2seconde pour initialisation de l'afficheur LCD affiche_lcd(1,premier_affiche); while(1); } 4.6.3. Simulation Le module SPI ne peut pas être simulé sous MPLAB IDE. Apres avoir compilé le projet, programmer la carte de développement et vérifier que le message s’affiche correctement. Pour aller un peu plus loin dans l’utilisation de l’afficheur LCD, vous pouvez développer une application qui affiche l’horloge courante, la valeur du potentiomètre et la température sur l’écran LCD. Le réglage de l’horloge peut se faire avec les boutons INT0 et INT1. Un tel projet a été développé dans le dossier projetLCD. Mais maintenant que vous avez créé tous les fichiers d’initialisations des périphériques, vous verrez que les temps de développement sont réduits.
5. Conclusion Grâce à tous ces minis modules nous avons pu tour à tour configurer les périphériques les plus usités dans le monde des microcontrôleurs. Toutes les fonctionnalités ne sont pas décrites mais elles vous permettent déjà de lancer une application. Le détail de chaque périphérique se trouve dans la documentation officielle de Microchip.
UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
64
6. Le noyau temps-réel PICOS 6.1. Présentation Les fonctionnalités de la carte dsPIC que nous venons de détailler laissent entrevoir la mise en œuvre de projets riches et variés, combinant l’utilisation et la mise en œuvre de différents ports de communication, et de périphériques multiples : affichage, contrôle moteur, etc… Le contrôle des différents périphériques devenant aisé à l’aide des blocs créés précédemment, on comprend que le défi devient de réussir à les faire fonctionner ensemble, de manière fiable, et pourquoi pas en intégrant des contraintes temps-réel. Nous avons donc des éléments à contrôler qui ne fonctionnent pas tous à la même cadence (cf les temps de latence de l’afficheur), dont les fonctions sont complémentaires et qui peuvent facilement être amenées à s’imbriquer entre elles. Un des moyens les plus simples pour mettre en œuvre rapidement un contrôle efficace et organisé de ces éléments est le recours à l’usage d’un noyau temps réel. Concrètement, un noyau temps-réel est un ensemble de procédures et de fonctions cohérent appelés services, qui selon le niveau de développement et de complexité, vont gérer la cohabitation de différentes tâches, gérer l’allocation des ressources matérielles, gérer les liens entre les différentes tâches, etc… 6.1.1. Les avantages d’un noyau temps-réel Un noyau temps-réel présente donc l’avantage de simplifier pour le développeur la mise en place de mécanismes de synchronisation, de priorités entre différentes fonctions. L’usage d’un noyau temps-réel normalisé est aussi garant d’une certaine sureté de fonctionnement : le code du noyau a été testé, vérifié, éprouvé : on conçoit donc plus facilement un programme fiable en se basant sur un code dont la justesse a été éprouvée. D’autre part, utiliser un noyau tempsréel impose aussi un cadre et un certain nombre de règles qui forcent le développeur à travailler proprement ce qui facilite aussi la maintenance. 6.1.2. Les inconvénients d’un noyau temps-réel Un noyau temps-réel présente un certain nombre d’inconvénients de fait : - il n’existe pas forcément de noyau temps-réel pour toutes les plates-formes. En effet, une des conditions sine qua non pour qu’un noyau temps-réel existe ou soit adaptable pour une plate-forme donnée est l’accès à la pile. En effet, le noyau doit pouvoir sauvegarder le contexte d’exécution d’une tâche et en charger un autre pour lancer ou poursuivre un seconde tâche, et cela nécessite de pouvoir lire et écrire dans les registres de la pile. - Le noyau ralentit l’exécution. De la même manière que la sauvegarde d’un contexte lors d’une interruption est une étape « inutile » (en ce sens qu’elle n’apporte rien à l’utilisateur et ne fait pas progresser le programme) mais nécessaire, l’usage d’un noyau temps-réel ralentit l’exécution d’un programme à cause de l’exécution de ses propres routines. - Le noyau occupe de la mémoire programme. Nécessairement, la mise en œuvre d’un noyau temps-réel occupe de l’espace mémoire qui n’est plus disponible pour l’application envisagée. Toutefois, lorsqu’une puce est susceptible de faire fonctionner un noyau temps-réel et dispose donc d’un jeu d’instructions assez fourni, il est rare que l’espace mémoire du microcontrôleur ne soit pas en proportion avec le niveau de la gamme, et ce facteur ne devrait pas être limitant dans un premier temps. Les cas où UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
65
-
ce point risque d’être rédhibitoire sont ceux où l’on est amené à stocker des grandes tables en mémoire (pilotage d’écran graphique, recours à des tables de sinus / cosinus, etc…) La mise en œuvre d’un noyau temps-réel nécessite un temps de prise en main par le développeur. Quand bien même un ouvrage présente de manière détaillée un noyau, une équipe de développement devra changer quelques habitudes pour s’adapter à un autre type de fonctionnement.
6.2. PICOS : un noyau temps-réel, multitâches, préemptif, normalisé 6.2.1. Un noyau temps-réel. Picos est un noyau dit temps-réel car on peut prédire de manière certaine le temps qui peut s’écouler entre l’exécution de deux instructions où qu’elles soient dans le code du programme. La version de Picos destinée aux PIC de la série 18F présente un temps de latence de 50us. Cela signifie que Picos garantit que le temps qui s’écoulera entre l’apparition d’un événement et sa prise en compte par le programme de l’utilisateur (si l’événement a été prévu, bien entendu) est inférieur à 50us. Picos est donc un système déterministe : on peut prévoir le temps nécessaire à l’exécution de telle ou telle opération, prévoir des temps de réponse, etc… 6.2.2. Un noyau multitâches Le noyau contrôle pilote les ressources du microcontrôleur et c’est lui qui va ordonner l’exécution du programme de l’utilisateur. C’est un noyau multitâches, il est donc capable d’exécuter plusieurs programmes simultanément (en réalité tour à tour, mais sans attendre que le premier soit terminé pour amorcer le second). Ces programmes sont appelés « tâches ». On peut programmer une tâche sans se soucier des autres tâches déjà existantes. Les autres tâches sont transparentes pour le développeur qui écrit son programme comme si les autres tâches n’existaient pas. Néanmoins, ces tâches ont la possibilité d’interagir entre elles grâce au concept d’événement. 6.2.3. Un noyau préemptif Picos est un noyau multitâche préemptif en ce sens que c’est le noyau qui garde en permanence « la main » sur les ressources processeur, et qu’il va exécuter la tâche la plus prioritaire sans attendre que la tâche en cours ait terminé. C’est ce principe qui garantit le temps de latence maximal de 50us : c’est le temps qui correspond grosso modo au temps nécessaire pour détecter qu’une tâche est plus prioritaire que celle qui est en cours d’exécution, à la sauvegarde du contexte de la tâche en cours, et au chargement du contexte de la tâche prioritaire dernièrement détectée. 6.2.4. Un noyau normalisé Cet aspect de norme en ce qui concerne Picos n’est pas encore totalement abouti. La norme OSEK-VDX utilisée dans l’automobile et la robotique définit des fonctionnalités regroupées en trois volets pour un noyau : Operating System (OS), Communication (COM) et Network Management (NM). Pour l'instant seule la partie OS a été implémentée. On peut donc s’appuyer sur PICOS en confiance puisque ce qui a été développé respecte la norme, mais il ne faut pas dans l’état actuel en attendre un support complet de la norme OSEK. D’autre part, UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
66
cet aspect ne présente pas que des inconvénients : le noyau est plus léger et plus flexible d’une part, et d’autre part cela facilite son intégration dans une plus grande variété de systèmes et d’applications. On pourrait donc dire que PICOS se situe à un point proche d’un optimum entre rigidité d’une norme et flexibilité d’une initiative particulière. 6.3. Mise en œuvre de PICOS 6.3.1. Objectif et attentes Dans le cadre du projet de l’UV TR57, notre objectif était d’évaluer le potentiel du noyau PICOS associé à la carte DsPIC décrite plus haut. Un des aspects handicapants de la démarche réside dans le fait qu’il n’existe pas de tutoriel décrivant la mise en œuvre de PICOS tel qu’il en existe pour PICOS18. Il s’agit d’utiliser ce dernier en l’adaptant au fur et à mesure à la situation rencontrée. Nous avons donc tâtonné au cours du semestre jusqu’à faire fonctionner PICOS, créer de nouvelles tâches, et parallèlement à cela maîtriser les périphériques de la carte DsPIC et notamment le contrôle moteur au moyen des sorties PWM. Ce rapport a donc été rédigé premièrement dans le but de faciliter et d’accélérer la mise en œuvre de PICOS pour le néophyte. 6.3.2. Etape n°1 : se procurer les outils nécessaires Manipuler PICOS nécessite les autorisations ou outils présentés ci-dessous : Outil / Autorisation Rôle Droits d’administrateur sur le Nécessaire pour installer les poste utilisé logiciels, et pour modifier certaines arborescences MPLAB IDE v7.5 Environnement de développement Compilateur C30 v2.5 édition étudiants
Compilateur C
PICOS v1.05
Nécessaire pour travailler
Où se le procurer Responsable de l’UV / CRI
http://www.microchip.com home > product > development tool http://www.microchip.com home > product > development tool (nécessité de s’enregistrer) http://www.picos18.com > download
Une fois tous ces outils téléchargés, nous allons procéder à l’installation. 6.3.3. Etape n°2 : installer et configurer les outils Nous ne reviendrons pas sur l’installation et la prise en main de MPLAB IDE. Après l’installation du compilateur C30, on vérifiera qu’il est bien pris en compte dans l’environnement de développement.
UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
67
Le fichier contenant picos téléchargé sur le site doit être décompressé dans un répertoire du disque dur, le plus proche possible de la racine afin d’éviter des problèmes d’espaces ou de caractères spéciaux dans l’arborescence. Dans cet exemple, nous utilisons : C:\PIC\ Après la décompression des fichiers, nous obtenons l’arborescence suivante :
Nous n’aurons pas de modification à faire dans les répertoires Kernel et Linker. Le premier contient les librairies du noyau, qui ne seront pas recompilées, tandis que le second contient les fichiers nécessaires au compilateur pour faire les liens entre les librairies et les pages de code à compiler. Le répertoire « Project » est celui qui est prévu pour accueillir les fichiers de projet. Nous y reviendrons dans le prochain paragraphe. Le dossier include est destiné à recevoir toutes les librairies supplémentaires que l’on serait susceptible d’ajouter au projet. Il contient déjà 5 fichiers : le gestionnaire d’alarme, le gestionnaire d’événements, le gestionnaire de programmes, le gestionnaire d’interruptions, et la bibliothèque s’adaptant au PIC cible choisi pour faire fonctionner PICOS. 6.3.4. Etape n°3 : création et paramétrage du projet PICOS Dans cette étape, nous allons créer le projet MPLAB qui va relier tous les éléments de PICOS. La marche à suivre est la suivante : - Se logguer comme administrateur - Démarrer MPLAB IDE - Créer un nouveau projet : Project > New Nous appellerons notre projet : « PEM01 » pour prise en main Le répertoire choisi pour le projet est celui qui est prévu à cet effet dans PICOS : C:\PIC\Project\ - Définir le compilateur à utiliser : Project > Set Language Toolsuite et dans la liste déroulante choisir « Microchip C30 Toolsuite »
UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
68
6.3.5. Etape n°4 : Ajouter les fichiers au projet Sélectionner le répertoire « Source Files » et à l’aide d’un clic droit, faire « Add Files… ». - Sélectionner le fichier main.c situé dans C:\PIC\Project\MyApp - Réitérer l’opération pour ajouter tasckdesc.c depuis C:\PIC\Project\MyApp - Réitérer l’opération pour ajouter tsk_task0.c depuis C:\PIC\Project\MyApp - Réitérer l’opération pour ajouter int_man.c depuis C:\PIC\Kernel Sélectionner le répertoire « Header Files » et à l’aide d’un clic droit, faire « Add Files… ». - Sélectionner le fichier define.h situé dans C:\PIC\Project\MyApp Sélectionner le répertoire « Library Files » et à l’aide d’un clic droit, faire « Add Files… ». L’arborescence du projet une Sélectionner le fichier PICos30.a situé dans C:\PIC\Kernel fois que tous les fichiers ont été correctement ajoutés Nous devons encore ajouter le fichier contenant le script du linker. Ce fichier n’est pas fourni avec PICOS, nous utilisons donc celui de Microchip. Attention à bien sélectionner le fichier correspondant au modèle du PIC cible. Dans notre cas, il suffit de sélectionner le répertoire « Linker Script » et d’ajouter le fichier « p30f4011.gld » à partir de : C:\Program Files\Microchip\MPLAB ASM30 Suite\Support\gld 6.3.6. Etape n°5 : Paramétrer les chemins des ressources Une fois que tous ces fichiers ont été ajoutés, il est nécessaire de configurer les chemins d’accès aux autres fichiers auxquels MPLAB pourra être amené à accéder, comme par exemple les fichiers du noyau qui sont utilisés en l’état sans recompilation : Cette opération est à réaliser de la manière suivante : Project > Build Options > Project > On affiche alors une fenêtre telle que celle-ci :
UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
69
Il s’agit d’inclure dans les chemins les emplacements suivants à l’aide de la fonction « browse » : Pour « Include Path » : - C:\Program Files\Microchip\MPLAB C30\include\ - C:\PIC\include Pour « Librairy Path » : - C:\Program Files\Microchip\MPLAB C30\lib - C:\PIC\Kernel
Attention !!! Pour inclure plusieurs chemins dans le champ, il faut les séparer par un point-virgule, mais surtout pas par un espace ! Si un espace se glisse entre deux champs, il n’y aura aucun message d’erreur, mais la compilation ne fonctionnera pas. C’est l’erreur la plus courante à éviter. Nous pouvons à présent vérifier que le projet est correctement configuré en lançant la compilation par la touche F10, puis en faisant un « Build » du projet par Ctrl+F10. Des « Warning » peuvent apparaître en raison de l’expiration des licences. Dans ce cas, le compilateur ne réalise pas toutes les optimisations de code possibles, mais ce n’est pas gênant car ces optimisations ne sont pas toutes compatibles avec PICOS.
6.4. Comprendre le fonctionnement de PICOS, visite guidée du code 6.4.1. Le fichier main.c Le fichier main.c comporte d’abord une série de commentaires relatifs à la licence GPL sous la quelle il est distribué. On note aussi la version et la date de création.
/**********************************************************************/ /* */ /* File name: main.c */ /* */ /* Since: 2005-April-18 */ /* */ /* Version: PICos v1.05 - RTOS for PIC24/30/33 families. */ /* Copyright (C) 2005-2006 Pragmatec. */ /* */ /* Author: Designed by Pragmatec S.A.R.L. www.pragmatec.net */ /* MONTAGNE Xavier [XM] [email protected] */ /* */
UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
70
L’historique des modifications est débuté sous les informations relatives à la licence. Nous devrions donc signaler ici toutes les modifications apportées au fichier.
/* Purpose: Custom initialization and kernel start. */ /* */ /* Distribution: This file is part of PICos. */ /* PICos is free software; you can redistribute it */ /* and/or modify it under the terms of the GNU General */ /* Public License as published by the Free Software */ /* Foundation; either version 2, or (at your option) */ /* any later version. */ /* */ /* PICos is distributed in the hope that it will be */ /* useful, but WITHOUT ANY WARRANTY; without even the */ /* implied warranty of MERCHANTABILITY or FITNESS FOR A */ /* PARTICULAR PURPOSE. See the GNU General Public */ /* License for more details. */ /* */ /* You should have received a copy of the GNU General */ /* Public License along with gpsim; see the file */ /* COPYING.txt. If not, write to the Free Software */ /* Foundation, 59 Temple Place - Suite 330, */ /* Boston, MA 02111-1307, USA. */ /* */ /* > A special exception to the GPL can be applied should */ /* you wish to distribute a combined work that includes */ /* PICos, without being obliged to provide the source */ /* code for any proprietary components. */ /* */ /* History: */ /* 2005/04/18 [XM] Create this file. */ /* */ /**********************************************************************/ #include "define.h" /********************************************************************** * Definition dedicated to the local functions. **********************************************************************/ #define DEFAULT_MODE 0 /********************************************************************** * Function prototypes. **********************************************************************/ int main (void); void Init(void); void StartupHook(void); void ShutdownHook(StatusType error); void ErrorHook(StatusType error); void PreTaskHook(void); void PostTaskHook(void); volatile AppModeType SelectedMode; volatile unsigned int __attribute__((__section__(".stack_kernel"))) stack_k[128]; volatile unsigned int W15_KERNEL, W14_KERNEL; volatile unsigned int kernelState;
Après la description des prototypes de fonctions et quelques instructions assembleur pour prendre le contrôle de la pile, on trouve l’appel de fonction « StartOS(SelectedMode) ». C’est cet appel qui démarre PICOS à proprement parler. Cet intéressant à noter car dans une phase de développement, si l’on veut tester le
/********************************************************************** * -------------------------- main function --------------------------* * Setup the different alarms and start the kernel. * **********************************************************************/ int main(void) { asm ("mov.w #_stack_k,w14" : : ); //WREG14 = (unsigned int)(stack_k); asm ("mov.w #_stack_k,w15" : : ); //WREG15 = (unsigned int)(stack_k); SelectedMode = DEFAULT_MODE; kernelState = STOP; Init(); IEC1bits.INT1IE = 1; IEC1bits.INT2IE = 1; StartOS(SelectedMode);
UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
71
fonctionnement d’une tâche seule alors que les autres tâches ne sont pas encore écrites, tout en restant dans le même projet, on pourra ne pas démarrer PICOS en plaçant cette ligne en commentaires, et à la place, on collera le code de la fonction que l’on souhaite tester. D’autre part, si nous voulons inclure une fonction primaire qui doit s’exécuter avant toutes les autres (par exemple pour empêcher le démarrage de PICOS si les tests de sécurité n’ont pas tous renvoyé un résultat négatif), on pourra inclure la fonction avant l’instruction StartOS. On peut ainsi imaginer toutes sortes de vérifications à opérer au démarrage avant la mise en fonction du système. On trouve également des fonctions qui pour l’instant n’ont pas d’effet : « PreTaskHook » et PostTaskHook ». Ces fonctions sont appelées respectivement juste avant d’entrer dans une tâche et juste après avoir quitté une tâche. On peut s’en servir par exemple pour visualiser sur une sortie numérique les changements de tâches. Cela peut constituer, dans une phase de test, un bon indicateur de la charge du noyau. C’est aussi un moyen de tester combien de temps la tâche nécessite pour son exécution : c’est le temps entre l’entrée et la sortie de la tâche. C’est donc un bon outil pour vérifier des aspects synchronisation. On peut aussi s’en servir pour détecter des plantages ou des erreurs dans les définitions de priorités: si le système ne change jamais de tâche alors qu’il le devrait, on peut s’en apercevoir en sortant sur une broche les appels çà ces deux fonctions.
return(0); } /********************************************************************** * Clear all RAM memory and set PORTB to output mode. * * @return void **********************************************************************/ void Init(void) { IEC0bits.T1IE = 1; W15_KERNEL = 0; W14_KERNEL = 0; /* interrupt automaticaly disabled during ISR */ //INTCON1bits.NSTDIS = 1; T1CON = 0xA010; T1CONbits.TON = 1; /* for debug only !!*/ INTCON2bits.ALTIVT = 1; // Set clock frequency (for 1 ms tick) PR1 = _16MHZ_PIC24; IEC0bits.T1IE = 1; IFS0bits.T1IF = 0; TMR1 = 0; InitTCB(); return; } /********************************************************************** * Hook routine called just before entering in kernel. * * @param error IN The new error stored in buffer * @return error Error level **********************************************************************/ void StartupHook(void) { } /********************************************************************** * Hook routine called just after leaving the kernel. * * @param error IN The last error detected by OS * @return void **********************************************************************/ void ShutdownHook(StatusType error) { } /********************************************************************** * Store a new error in a global buffer keeping a track of the * application history. * * @param error IN The new error stored in buffer * @return void **********************************************************************/ void ErrorHook(StatusType error) { } /********************************************************************** * Hook routine called just before entering in a task. * * @return void **********************************************************************/ void PreTaskHook(void) { } /**********************************************************************
UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
72
* Hook routine called just after leaving a task. * * @return void **********************************************************************/ void PostTaskHook(void) { } /* End of File : main.c */
6.4.2. Le fichier Tascdesc.c Le fichier tascdesc regroupe un certain nombre d’informations sur toutes les tâches gérées par le système.
/**********************************************************************/ /* */ /* File name: tascdesc.c */ /* */ /* Since: 2005-April-18 */ /* */ /* Version: PICos v1.05 - RTOS for PIC24/30/33 families. */ /* Copyright (C) 2005-2006 Pragmatec. */ /* */ /* Author: Designed by Pragmatec S.A.R.L. www.pragmatec.net */ /* MONTAGNE Xavier [XM] [email protected] */ /* */ /* Purpose: Kind of OIL file where all the features of the tasks */ /* are described. */ /* */ /* Distribution: This file is part of PICos. */ //Informations relatives à la licence /* PICos, without being obliged to provide the source */ /* code for any proprietary components. */ /* */ /* History: */ /* 2005/04/18 [XM] Create this file. */ /* */ /**********************************************************************/
Après les classiques informations de licence et d’historique, on trouve le détail du système de déclenchement des alarmes.
Le concept des alarmes est décrit dans le tutorial de PICOS18. Notre objectif est ici de clarifier les éléments qui diffèrent de PICOS18 afin de faciliter la prise en main de PICOS.
#include "define.h" /********************************************************************** * --------------------- COUNTER & ALARM DEFINITION ------------------**********************************************************************/ volatile Counter Counter_list[] = { /******************************************************************* * -------------------------- First counter -----------------------*******************************************************************/ { { 65535, /* maxAllowedValue */ 1, /* ticksPerBase */ 0 /* minCycle */ }, 0, /* CounterValue */ 0 /* Nbr of Tick for 1 CPT */ } }; volatile Counter Counter_kernel = { { 65535, /* maxAllowedValue */ 1, /* ticksPerBase */ 0 /* minCycle */ }, 0, /* CounterValue */ 0 /* Nbr of Tick for 1 CPT */ };
UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
73
La description de chaque tâche se présente ainsi : On distingue deux parties. Dans une première partie, on définit les paramètres relatifs aux compteurs et aux alarmes, pour chaque tâche.
volatile AlarmObject Alarm_list[] = { /******************************************************************* * -------------------------- First task --------------------------*******************************************************************/ { OFF, /* State */ 0, /* AlarmValue */ 0, /* Cycle */ (RefCounter)&Counter_kernel, /* ptrCounter */ TASK0_ID, /* TaskID2Activate */ ALARM_EVENT, /* EventToPost */ NULL /* CallBack */ }, /******************************************************************* * -------------------------- Second task --------------------------*******************************************************************/ { OFF, /* State */ 0, /* AlarmValue */ 0, /* Cycle */ (RefCounter)&Counter_kernel, /* ptrCounter */ TASK1_ID, /* TaskID2Activate */ TASK1_EVENT, /* EventToPost */ NULL /* CallBack */ }, /******************************************************************* * -------------------------- third task --------------------------*******************************************************************/ { OFF, /* State */ 0, /* AlarmValue */ 0, /* Cycle */ (RefCounter)&Counter_kernel, /* ptrCounter */ TASK2_ID, /* TaskID2Activate */ TASK2_EVENT, /* EventToPost */ NULL /* CallBack */ }, }; #define _ALARMNUMBER_ sizeof(Alarm_list)/sizeof(AlarmObject) #define _COUNTERNUMBER_ sizeof(Counter_list)/sizeof(Counter) unsigned char ALARMNUMBER = _ALARMNUMBER_; unsigned char COUNTERNUMBER = _COUNTERNUMBER_; unsigned long global_counter = 0; /********************************************************************** * ----------------------- RESOURCE DEFINITION -----------------------**********************************************************************/ volatile Resource Resource_list[] = { /* ADC for example */ { 10, /* priority */ 0, /* Task prio */ 0, /* lock */ }, }; #define _RESOURCENUMBER_ sizeof(Resource_list)/sizeof(Resource) unsigned char RESOURCENUMBER = _RESOURCENUMBER_;
Puis on peut définir la taille de la pile à affecter à la tâche. La valeur proposée par défaut de 128 convient dans la plupart des cas. Plus la pile est grande, plus le temps nécessaire pour « switcher » d’une tâche à une autre est important. A l’inverse, si la pile est
/********************************************************************** * ----------------------- TASK & STACK DEFINITION -------------------**********************************************************************/ #define DEFAULT_STACK_SIZE 128 // Warning : in 16 bits not 8 bits ! DeclareTask(TASK0); DeclareTask(TASK1); DeclareTask(TASK2); u_int __attribute__((__section__(".stack_tsk0"))) stack0[DEFAULT_STACK_SIZE];
UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
74
choisie trop petite, la tâche va déborder la pile et planter le système. Un moyen de vérifier le comportement en cas de dépassement de la pile est de mettre en place un algorithme récursif infini ou très long. Enfin on trouve les paramètres les plus utiles et que l’on peut être amené à modifier plus couramment. On relèvera notamment : la priorité, la taille de la pile, l’adresse de la pile, l’événement déclencheur, l’état de la tâche…
Dans cet exemple, nous avons trois tâches : 0, 1 et 2 qui cohabitent.
u_int __attribute__((__section__(".stack_tsk1"))) stack1[DEFAULT_STACK_SIZE]; u_int __attribute__((__section__(".stack_tsk2"))) stack2[DEFAULT_STACK_SIZE]; /********************************************************************** * ---------------------- TASK DESCRIPTOR SECTION --------------------**********************************************************************/ const unsigned int descromarea; const TCB TCB_const_list[] = { /******************************************************************* * -------------------------- Task 0 ------------------------------*******************************************************************/ { SREG(stack0), /* Stack_register */ FREG(stack0), /* Frame_register */ TASK0, /* StartAddress */ stack0, /* StackAddress */ sizeof(stack0), /* StackSize */ TASK0_ID, /* TaskID */ TASK0_PRIO, /* Priority */ NONE, /* EventWaited */ NONE, /* EventReceived */ READY, /* State */ EXTENDED, /* Type */ 0, /* Time */ 0, /* kernelState_copy */ NULL /* next */ }, /******************************************************************* * -------------------------- Task 1 ------------------------------*******************************************************************/ { SREG(stack1), /* Stack_register */ FREG(stack1), /* Frame_register */ TASK1, /* StartAddress */ stack1, /* StackAddress */ sizeof(stack1), /* StackSize */ TASK1_ID, /* TaskID */ TASK1_PRIO, /* Priority */ NONE, /* EventWaited */ NONE, /* EventReceived */ READY, /* State */ EXTENDED, /* Type */ 0, /* Time */ 0, /* kernelState_copy */ NULL /* next */ }, /******************************************************************* * -------------------------- Task 2 ------------------------------*******************************************************************/ { SREG(stack2), /* Stack_register */ FREG(stack2), /* Frame_register */ TASK2, /* StartAddress */ stack2, /* StackAddress */ sizeof(stack2), /* StackSize */ TASK2_ID, /* TaskID */ TASK2_PRIO, /* Priority */ NONE, /* EventWaited */ NONE, /* EventReceived */ READY, /* State */ EXTENDED, /* Type */ 0, /* Time */ 0, /* kernelState_copy */ NULL /* next */ }, }; volatile unsigned int taskdesc_addr = (unsigned int)(TCB_const_list); #define _TASKNUMBER_ sizeof(TCB_const_list)/sizeof(TCB) unsigned char TASKNUMBER = _TASKNUMBER_; volatile TCB TCB_list[_TASKNUMBER_]; /* End of File : taskdesc.c */
UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
75
6.4.3. Le fichier tsk_taskX.c Ce fichier doit contenir le code de la tâche proprement dite. Après les traditionnelles informations de licence et d’historique, on trouve dans la fonction « TASK(TaskX) » le code du programme de la tâche. On prendra garde, si l’on veut que la tâche s’exécute en permanence à inclure une boucle infinie. En effet, cette boucle ne bloquera pas le système puisque PICOS reprendra la main régulièrement pour traiter les autres tâches. On peut facilement vérifier ceci en plaça un breakpoint sur la boucle.
/**********************************************************************/ /* */ /* File name: tsk_task0.c */ /* */ /* Since: 2005-April-18 */ /* */ /* Version: PICos v1.05 - RTOS for PIC24/30/33 families. */ /* Copyright (C) 2005-2006 Pragmatec. */ /* */ /* Author: Designed by Pragmatec S.A.R.L. www.pragmatec.net */ /* MONTAGNE Xavier [XM] [email protected] */ /* */ /* Purpose: First task of the tutorial. */ /* */ /* Distribution: This file is part of PICos. */ /* PICos is free software; you can redistribute it */ /* and/or modify it under the terms of the GNU General */ /* Public License as published by the Free Software */ /* Foundation; either version 2, or (at your option) */ /* any later version. */ /* */ /* PICos is distributed in the hope that it will be */ /* useful, but WITHOUT ANY WARRANTY; without even the */ /* implied warranty of MERCHANTABILITY or FITNESS FOR A */ /* PARTICULAR PURPOSE. See the GNU General Public */ /* License for more details. */ /* */ /* You should have received a copy of the GNU General */ /* Public License along with gpsim; see the file */ /* COPYING.txt. If not, write to the Free Software */ /* Foundation, 59 Temple Place - Suite 330, */ /* Boston, MA 02111-1307, USA. */ /* */ /* > A special exception to the GPL can be applied should */ /* you wish to distribute a combined work that includes */ /* PICos, without being obliged to provide the source */ /* code for any proprietary components. */ /* */ /* History: */ /* 2005/04/18 [XM] Create this file. */ /* */ /**********************************************************************/ #include "define.h" /********************************************************************** * Definition dedicated to the local functions. **********************************************************************/ #define ALARM_TSK0 0 /********************************************************************** * ------------------------------ TASK0 ------------------------------* * First task of the tutorial. * **********************************************************************/ TASK(TASK0) { TRISBbits.TRISB4 = 0; LATBbits.LATB4 = 0; SetRelAlarm(ALARM_TSK0, 1000, 1000); while(1) { WaitEvent(ALARM_EVENT); ClearEvent(ALARM_EVENT); LATBbits.LATB4 = ~LATBbits.LATB4; } } /* End of File : tsk_task0.c */
UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
76
6.5. Création d’une nouvelle tâche La création d’une nouvelle tâche dans PICOS nécessite de modifier ou d’ajouter des paramètres à différents endroits des fichiers que nous venons de décrire. Nous allons ajouter ensemble une nouvelle tâche au projet, étape par étape. 6.5.1. Création d’un nouveau fichier tsk_taskX.c Notre nouvelle tâche sera écrite dans un nouveau fichier tsk_task.c. Pour cela, nous faisons une copie du fichier existant, à l’aide de l’explorateur par exemple, et on renomme le fichier en changeant le numéro. Pour plus de clarté, on n’hésitera pas à modifier l’entête du fichier en indiquant clairement le rôle de la tâche, éventuellement ses conditions de déclenchement et ou les événements qu’elle peut déclencher. On ajoute ensuite ce fichier tsk_task1.c au projet. Dans ce fichier, il faut maintenant modifier le nom de la tâche dans la fonction. On remplacera donc TASK(TASK0) {
/#define TASK0_EVENT
Par
Par
TASK(TASK1) {
/#define TASK1_EVENT
0
0
On supprimera ou on mettra en commentaire pour le moment l’instruction : #define ALARM_TSK0
0
Le fichier tsk_task1.c est donc prêt pour la compilation. Evidemment, on n’oubliera pas d’ajouter le code de la fonction remplie par la tâche. ! 6.5.2. Modifications dans le fichier taskdesc.c Nous allons apporter des modifications au fichier taskdesc.c afin d’y déclarer la nouvelle tâche. Etape n°1 : copier et coller à la suite le passage : /******************************************************************* * -------------------------- First task --------------------------*******************************************************************/ { OFF, /* State */ 0, /* AlarmValue */ 0, /* Cycle */ (RefCounter)&Counter_kernel, /* ptrCounter */ TASK0_ID, /* TaskID2Activate */ ALARM_EVENT, /* EventToPost */ NULL /* CallBack */ }, UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
77
On remplace ensuite les informations suivantes : - le titre « First Task » devient « Second Task ». Il peut aussi prendre un nom beaucoup plus parlant : « Contrôle moteur » ou « Affichage ». - TASK0_ID devient « TASK1_ID » Plus loin dans le fichier taskdesc, on trouve le paragraphe « Task & Stack definition ». On va ici déclarer la tâche. Le paragraphe avant modification ressemble à ceci : #define DEFAULT_STACK_SIZE DeclareTask(TASK0);
128 // Warning : in 16 bits not 8 bits !
u_int __attribute__((__section__(".stack_tsk0"))) stack0[DEFAULT_STACK_SIZE]; On le transforme afin qu’il ressemble à cela : #define DEFAULT_STACK_SIZE DeclareTask(TASK0); DeclareTask(TASK1);
128 // Warning : in 16 bits not 8 bits !
u_int __attribute__((__section__(".stack_tsk0"))) stack0[DEFAULT_STACK_SIZE]; u_int __attribute__((__section__(".stack_tsk1"))) stack1[DEFAULT_STACK_SIZE]; Enfin, il reste à modifier le descripteur de tâche en ajoutant la nouvelle tâche. On s’aidera pour cela d’un copier-coller. Voici donc le paragraphe avant modification : /********************************************************************** * ---------------------- TASK DESCRIPTOR SECTION --------------------**********************************************************************/ const unsigned int descromarea; const TCB TCB_const_list[] = { /******************************************************************* * -------------------------- Task 0 ------------------------------*******************************************************************/ { SREG(stack0), /* Stack_register */ FREG(stack0), /* Frame_register */ TASK0, /* StartAddress */ stack0, /* StackAddress */ sizeof(stack0), /* StackSize */ TASK0_ID, /* TaskID */ TASK0_PRIO, /* Priority */ NONE, /* EventWaited */ NONE, /* EventReceived */ READY, /* State */ EXTENDED, /* Type */ 0, /* Time */ 0, /* kernelState_copy */ NULL /* next */ UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
78
}, }; Après modification, on obtient ceci : /********************************************************************** * ---------------------- TASK DESCRIPTOR SECTION --------------------**********************************************************************/ const unsigned int descromarea; const TCB TCB_const_list[] = { /******************************************************************* * -------------------------- Task 0 ------------------------------*******************************************************************/ { SREG(stack0), /* Stack_register */ FREG(stack0), /* Frame_register */ TASK0, /* StartAddress */ stack0, /* StackAddress */ sizeof(stack0), /* StackSize */ TASK0_ID, /* TaskID */ TASK0_PRIO, /* Priority */ NONE, /* EventWaited */ NONE, /* EventReceived */ READY, /* State */ EXTENDED, /* Type */ 0, /* Time */ 0, /* kernelState_copy */ NULL /* next */ }, /******************************************************************* * -------------------------- Task 1 ------------------------------*******************************************************************/ { SREG(stack1), /* Stack_register */ FREG(stack1), /* Frame_register */ TASK1, /* StartAddress */ stack1, /* StackAddress */ sizeof(stack1), /* StackSize */ TASK1_ID, /* TaskID */ TASK1_PRIO, /* Priority */ NONE, /* EventWaited */ NONE, /* EventReceived */ READY, /* State */ EXTENDED, /* Type */ 0, /* Time */ 0, /* kernelState_copy */ NULL /* next */ }, }; UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
79
6.5.3. Modifications dans le fichier define.h Le fichier define.h contient un certain nombre de constantes. Notamment, il contient les constantes comme « TASKx_ID et « TASKx_PRIO ». On va donc déclarer les nouvelles constantes. C’est également ici que l’on définit la priorité de chaque tâche. On va donc transformer la fin du fichier define.h de ceci : *********************************************************************** * ----------------------------- Task ID ------------------------------**********************************************************************/ #define TASK0_ID 1 #define TASK0_PRIO
7
#endif /* _DEFINE_H */
En ceci : *********************************************************************** * ----------------------------- Task ID ------------------------------**********************************************************************/ #define TASK0_ID 1 #define TASK1_ID 2 #define TASK0_PRIO #define TASK1_PRIO
7 8
#endif /* _DEFINE_H */ Nous admettons ici que la tâche 1 est moins prioritaire que la tâche 0. A présent toutes les modifications minimales nécessaires à la prise en compte de la nouvelle tâche sont réalisées. Une compilation du projet devrait se dérouler sans erreur si la fonction est écrite correctement. Des erreurs peuvent apparaître si des fonctions portent le même nom dans des tâches différentes. Par exemple, si une fonction de la tâche 1 s’appelle « initialisation », on préférera utiliser d’autres noms pour les fonctions des autres tâches. 6.6. Exemple de fonction pilotée par PICOS : contrôle moteur par PWM Dans cette partie, on souhaite tout simplement réaliser le contrôle d’un moteur électrique en utilisant les sorties PWM. L’objectif est de montrer comment la mise au point d’une telle fonction s’intègre dans l’environnement de PICOS. Nous distinguerons deux étapes : une étape dite de « développement » et une étape dite de « fonctionnement ». Dans la première étape, on se place dans une situation où l’on souhaite développer la fonction et la tester aussi facilement que si on n’utilisait pas PICOS. Dans la seconde, on intègre la fonction validée lors de l’étape précédente dans le système d’exploitation. UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
80
6.6.1. Phase de développement Lors de la phase de développement, on souhaite pouvoir tester la fonction de la manière la plus simple possible. Or si on écrit directement cette fonction comme une tâche, il est possible que des inexactitudes de code liées à l’intégration dans PICOS viennent masquer des dysfonctionnements de la fonction que l’on cherche à résoudre. Pourquoi ne pas développer chaque tâche dans un projet séparé ? Cette question vient naturellement à l’esprit. Développer directement dans le projet final présente quelques avantages : - les constantes définies pour tout le projet et communes aux autres tâches sont accessibles, pas besoin de réécrire un nouveau fichier define.h - on s’aperçoit plus facilement si les modifications ou les choix (notamment en termes de noms de variables, de fonctions ou de constantes) que l’on fait pour la nouvelle fonction sont judicieux ou non : en cas de redondance, on aura tout de suite une erreur de compilation, dont la source sera aisément identifiable. - Les paramètres du microcontrôleur sont déjà configurés : choix de l’oscillateur (externe / interne), watchdog désactivé, etc… - L’espace mémoire nécessaire à PICOS sera déjà réservé, ce qui peut éviter des mauvaises surprises à l’assemblage du projet Comment faire ? On va développer la nouvelle fonction dans le fichier « main.c », sans démarrer PICOS. Pour cela, on met en commentaires l’instruction « StartOS(SelectedMode) » Ensuite, on peut ajouter le code à tester comme ci-dessous (le code en test est grisé) : int main(void) { asm ("mov.w #_stack_k,w14" : : ); //WREG14 = (unsigned int)(stack_k); asm ("mov.w #_stack_k,w15" : : ); //WREG15 = (unsigned int)(stack_k); SelectedMode = DEFAULT_MODE; kernelState = STOP; Init(); IEC1bits.INT1IE = 1; IEC1bits.INT2IE = 1; // StartOS(SelectedMode); TRISE = 0xFFC0; // PWM pins as outputs, and FLTA as input PTPER = (FCY/(FPWM) - 1) >> 1; // Compute Period based on CPU speed and PTCON = 0x0002; // required PWM frequency (see defines) OVDCON = 0x0000; // Disable all PWM outputs. DTCON1 = 0x0000; // 0 us of dead time => control électronique onboard PWMCON1 = 0x0033; // Enable PWM output pins and configure them as // complementary mode PDC1 = PTPER; // Initialize as 0 voltage PDC2 = PTPER; // Initialize as 0 voltage SEVTCMP = 0; // Disable triggering for ADC PWMCON2 = 0x0000; UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
81
PTCON = 0x8002; PDC1=PTPER/2; PDC2=PTPER/2; OVDCON = 0xFF00; while(1); return(0); } Lorsque le code est fonctionnel, on peut ensuite l’intégrer dans l’environnement de PICOS. On pensera à inclure l’instruction while(1) afin de visualiser quelque chose. 6.6.2. Intégration du code dans l’environnement de PICOS Notre code utilise des constantes, telles que la fréquence PWM, ou la fréquence de calcul. Ces valeurs sont susceptibles d’être utilisées dans d’autres fonctions, par exemple pour déterminer des temps de latence à mettre en œuvre pour communiquer avec des matériels lents. On a donc intérêt à définir ces constantes pour tout le projet. On va donc déclarer FCY et FPWM comme constantes dans le fichier define.h. Ce fichier define.h devient donc : /*********************************************************************** * ------------------------ Timer settings ----------------------------**********************************************************************/ #define _08MHZ_PIC30 250 #define _10MHZ_PIC30 375 #define _16MHZ_PIC30 500 #define _20MHZ_PIC30 750 #define _32MHZ_PIC30 1000 #define _40MHZ_PIC30 1250 #define _59MHZ_PIC30 1840 // 58,9824 (for MCP demo board with 8PLL) #define _64MHZ_PIC30 2000 #define _80MHZ_PIC30 2500 #define _96MHZ_PIC30 3000 #define _120MHZ_PIC30 3750 #define _40MHZ_PIC33 #define _16MHZ_PIC24
5250 // Explorer 16 Board with PLL, Prescale 1:8 2000 // Explorer 16 Board with PLL, Prescale 1:8
#define FCY 14745600 #define FPWM 20000 Ensuite, on crée une nouvelle tâche comme décrit dans les paragraphes précédents. Enfin, on copie le code de la fonction dans la nouvelle tâche, et on réactive l’instruction StartOS(SelectedMode) dans le fichier main.c. On fera attention à déclarer la tâche comme « ready » dans le fichier taskdesc.c si on veut la voir fonctionner.
UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
82
7. Conclusion PICOS est un système d’exploitation temps-réel dont nous avons pu valider les aspects suivants : - fonctionnement correct sur les PIC30F de la carte dsPIC. - Relative flexibilité dans le développement : PICOS n’est pas un obstacle à une phase de tâtonnement liée au matériel - Facilité de mise en œuvre : lorsque l’on a réussi à compiler une première fois le projet, puis lorsqu’on sait ajouter une tâche, PICOS devient quasiment transparent et n’engendre aucun temps de développement supplémentaire en comparaison d’un fonctionnement sans OS. - Multi-usage possible : la possibilité d’appeler des fonctions avant ou après les changements de tâches, ou bien lors de l’initialisation, étend encore les possibilités de PICOS. Si un développeur peut parfois avoir l’impression d’abandonner la maîtrise des événements en recourant à un noyau temps-réel tiers, cette impression ne se ressent pas trop lors de la mise en œuvre de PICOS : le système est assez compact et ouvert pour que son fonctionnement soit assimilable facilement, et d’autre part il intègre de nombreuses « portes d’entrées » supplémentaires permettant de garder la maîtrise des événements. Nous n’avons pas pu valider, faute de temps, les caractéristiques suivantes : - fonctionnement à pleine charge : comment le noyau réagit-il lorsque le nombre de tâches augmente ? Quel est réellement l’impact de la priorité définie dans taskdesc ? - les arcanes de la communication entre tâches : nous n’avons pas eu le temps de mettre en œuvre des tâches qui nécessitent une communication avancée entre elles. Les fonctionnalités de PICOS dans ce domaine restent à explorer. Au regard des objectifs qui nous étaient fixés, on peut répondre sans hésitation quant à l’intérêt d’un système comme PICOS. Notamment, dans le cadre de développements liés à la carte dsPIC, PICOS est une aubaine pour permettre la cohabitation de périphériques lents et de périphériques rapides de manière fiable et optimisée. L’utilisation des fonctions de pilotage du matériel décrites plus haut, que l’on pourrait presque assimiler à des drivers, combinée avec la mise en œuvre de PICOS, fait de l’ensemble une plate-forme de développement de choix particulièrement efficace, et permettant de développer rapidement des applications complexes. En effet, lorsque tous les tâtonnements sont effectués, d’un côté pour piloter le matériel, de l’autre pour assimiler une couche logicielle tierce, le développement d’une application est grandement simplifié. Ces travaux de découverte, de tâtonnements, d’essais et de prise en main nous ont fait découvrir les possibilités d’un noyau temps-réel et les avantages que cela procure. On a aussi pu noter que dans cette phase de développement, l’avancée du travail n’est pas toujours proportionnelle au temps passé. C’est la pratique de ce genre d’exercice qui développe l’intuition et la capacité à trouver au plus vite « pourquoi ça ne fonctionne pas ». Enfin, au terme de ce rapport, nous sommes heureux de mettre à la disposition d’autres étudiants « le » document dont nous aurions rêvé, qui propose une introduction au matériel et au logiciel permettant de commencer rapidement le développement d’une véritable application tempsréel.
UTBM – TR57 Automne 2006 – Eric OBSER & Vincent HAEGELIN – dSPIC30F4011 & PICOS 30
83