On peut qualifier l’application de multitâche dans le sens où plusieurs tâches de niveaux de priorités différents doivent s’exécuter en parallèle. Les trois tâches principales sont par ordre de priorité (de la plus forte à la plus faible) :
De leur nature, il ressort que les deux premières catégories doivent être des activités temps réel, c’est à dire des activités dont la vitesse de réponse ou d’exécution est inférieure à la base temporelle de l’expérience. Le système expérimental étant modulé à 300 Hz, l’objectif visé de la réponse proche de la milliseconde permet de qualifier le processus d’asservissement comme étant un processus temps réel. En ce qui concerne l’acquisition des données par la carte, les données arrivent dans la mémoire à flot continu via un canal DMA. Donc, et à moins que le bus ne soit surchargé , la vitesse par défaut d’échantillonnage fixée à 5 kHz donne des résultats parfaits. La lecture, s’effectue par rafale à la même vitesse que l’asservissement, donc par conséquent à une période proche de la milliseconde.
Notion de tâches prioritaires
Les propriétés des trois catégories cités sont peut-être l’aspect le plus intéressant du projet. Il est clair que l’acquisition, à la base de toutes les opérations effectuées par l’ordinateur est la tâche de plus haute priorité. Ayant choisi, l’acquisition par canal DMA (du fait de sa rapidité relative, cf. suite), le transfert des données numérisées s’effectue directement vers la mémoire sans transiter par le microprocesseur. L’avantage y est double : ne pas mobiliser la CPU inutilement (une précision de l’ordre de l’échantillon n’a aucune valeur d’un point de vue physique), s’assurer d’avoir une priorité supérieure à celle du reste de l’application ou même des applications lentes du système (l’IRQ de la carte est le canal 7).
L’asservissement et le diagnostic sont des tâches qui seront appelées de manière préemptive. Effectivement, pour assurer un effet de ‘tâche de fond’ indépendant des actions utilisateur ou du fonctionnement du système, j’ai écrit (nous verrons comment) un scheduleur particulier — de fonctionnement intermédiaire entre la préemptivité et le partage de ressources — utilisant le timer du PC. La période fixée à une milliseconde s’avère, à l’expérience, avoir une latence de quatre millisecondes, ce qui reste quand même acceptable.
Enfin, l’affichage est la partie du logiciel à laquelle j’ai décidé d’accorder le moins de resources système possible. Pour réaliser cette mission, j’ai voulu afficher les courbes et autres éléments graphiques uniquement lorsque la CPU est libre. En fait, puisque l’acquisition est un processus constant en termes de durée d’exécution, la CPU est libre lorsque la pile des messages système est vide et que l’on se situe entre deux interruptions logicielles de la tâche d’asservissement/diagnostic.
Les limites des appareils
Le PC, un 486 DX2 - 66, que l’on estimait au début comme présentant des caractéristiques très supérieures aux capacités exigées par la manipulation, par suite des nombreux ajouts au logiciel, s’avère de puissance tout juste suffisante. Windows, on le sait est un système encore peu performant en coûts CPU, cependant la couche écrite par mes soins permet de passer au-dessus du noyau et du mode de fonctionnement lent de Windows. Seul l’affichage risque d’être sacadé lors d’une simulation.
Bien que cela n’entre pas dans le cadre de l’application, j’aimerais également souligner le fait que la DAS1601, dont les performances d’échantillonnage annoncées sont de 100 kHz, ne s’avère en fait pas capable de monter au-dessus de 30 kHz sous Windows même en mode DMA. Cette fréquence chute même à 17 kHz dans le cas ou l’on voudrait appliquer un gain (interne à la carte) de 500.
Problèmes et résolutions
La configuration choisie pour réaliser la manipulation, bien que proposant des performances et un confort séduisants sur papier, n’est certainement pas la solution la plus simple à mettre en place. Les éléments incriminables sont aussi bien les appareils non adaptés à un fonctionnement en temps réel que le système utilisé. Le défit fut toutefois passionnant à relever.
Windows, un système non préemptif
Quotidiennement, que ce soit en imprimant, en formattant un disque, en lançant un calcul complexe avec son tableur, etc., l’utilisateur de Windows 16 bits subit la non préemptivité du système. Sans m’y attarder, je rappellerai son fonctionnement. Tout évènement au sein du système est signalé par l’envoi d’un ou plusieurs messages. L’application qui doit traiter un message donné, l’intercepte et effectue une opération en réponse. Toutefois, durant toute l’opération où l’application gère le message, les autres programmes attendent de récupérer la main. Pour résumer, la commutation de tâche s’effectue entre chaque message. Ce mode de fonctionnement, par l’aspect aléatoire d’intervalle de temps qui sépare deux messages, est totalement en désaccord avec les nécéssités d’un processus temps réel. C’est pourquoi, par choix, dans COPHA, seul l’affichage des oscilloscopes et des résultats utilise cette technique.
Windows, un système non temps réel
La technique que j’ai utilisée pour intégrer une couche temps réel au système, fait appel aux librairies multimédia de Windows. Un timer programmable est en effet capable d’appeler une fonction callback à espace temporel régulier ou au coup par coup. Dans le cas de l’asservissement, j’ai préféré recréer un mode intermédiaire ; ce mode n’appelle pas la fonction callback précisément toutes les millisecondes mais rappelle la fonction exactement une milliseconde après la fin du traitement d’asservissement.
Ce choix est basé sur la constatation suivante : le temps d’asservissement et de diagnostic est proportionnel au nombre d’échantillons à traiter. Par conséquent, si le temps de traitement est supérieur à une période du timer, il y aura une augmentation indéfinie du nombre d’échantillons en entrée. La conséquence de ce problème est une saturation des appels de la fonction callback (étant donné qu’il n’y a pas de la part de Windows de masquage de l’interruption timer). Grâce à la méthode mise en oeuvre et aux performances de l’ordinateur, la pratique montre que dans le pire des cas on atteint une lattence de quatre millisecondes.
La fonction en elle même est différente selon l’étape de la simulation. Dans tous les cas, elle effectue ses calculs sur les échantillons encore non traités.
L’acquisition des données
La DAS1601 mis à part les problèmes déjà évoqués de largeur du buffer, provoque bon nombre d’autres erreurs. Ainsi, régulièrement, quand on fonctionne à des débits relativement importants et qu’un conflit sur le bus intervient, la carte arrête l’échantillonnage. Le seul moyen de prévenir ce genre de situation est de tester par la technique du polling (effectué toute les cinq millisecondes) l’état de la carte. En cas de détection d’un break, il s’agit de relancer la carte. Mais un autre problème surgit : en faisant redémarrer l’échantillonnage, la carte se replace sur le premier octet du buffer tournant , entraînant ainsi des aberrations d’affichage. La seule solution que j’ai trouvée est de décaler la mémoire du buffer de manière à ce que le dernier octet correctement acquis soit le dernier du buffer.
Un autre problème de la carte est de multiplexer les échantillons des différentes voies. Ainsi, dans le cas où l’on veut écrire une classe d’accès aux données pour un nombre non fixe de voies actives, on est obligé d’effectuer une multiplication sur l’index. Ceci dégrade bien évidement les performances générales du système par rapport à une méthode qui aurait consisté à assigner des buffers différents à chacune des voies.
Enfin, lors d’essais d’incorporation de la vidéo, la surcharge du bus entraînait pratiquement toute les cinq secondes une rotation des voies d’échantillonnage : le multiplexeur se décale. La seule et unique solution à ce problème indétectable était de stopper la simulation et de la redémarrer.
La vitesse d’affichage
Les fenêtres oscilloscopes de l’application sont au nombre de trois mais affichent les quatre signaux en entrées des voies de la carte d’acquisition. Afficher une telle quantité d’information le plus rapidement possible demande d’optimiser les techniques d’affichage. Sans oublier que nous sommes dans un environnement multifenêtré, je propose dans COPHA, deux méthodes d’affichage. L’affichage reste unique mais la technique employée diffère :
La transmission d’informations entre les tâches
Comme expliqué dans la documentation multimédia de Windows, la fonction callback de réponse au timer utilisée pour créer une tâche de fond n’a pas le droit d’effectuer des appels systèmes. Cela s’explique par le fait que Windows n’est pas un système multitâche et les fonctions du Kernel, User et GDI ne sont pas réentrantes.
La technique que j’utilise pour communiquer avec l’application et donc aussi avec l’utilisateur depuis cette fonction de traitement en arrière plan, est celle de l’envoi de message. En raison d’une francisation toujours hasardeuse des termes informatiques, je préfère préciser qu’il s’agit de poster (PostMessage) et non pas porter (SendMessage) le message de notification. La différence réside dans le fait qu’il s’agit, dans le cas du PostMessage, de placer le message dans la pile FIFO de l’application. Bien évidement, il s’agit là d’une communication asynchrone, mais dans notre cas de notification et parce que je date tous les messages postés, cela ne pose pas de problème.
L’accès simultané aux ressources
Comme je l’ai déjà évoqué, Windows 3.1 n’est pas un système préemptif. L’accès simultané aux ressources n’est donc pas un problème connu dans cet environnement. L’asservissement, effectué en tâche de fond préemptive, n’aurait pas introduit de problème de conflits si les ressources matérielles n’avaient pas dû être partagées avec le module principal. Pour prendre l’exemple le plus flagrant d’accès simultané qu’introduit ce processus en arrière plan, je citerai le partage de la lecture de la position de la platine. En dehors de la simulation, seule la télécommande s’informe de la position de la platine. Par contre, pendant la simulation, ces coordonnées sont lues non seulement par la télécommande mais aussi par le processus de recherche de franges. Et puisque l’appel de la fonction callback de recherche de franges peut s’effectuer à n’importe quel moment (donc également pendant l’exécution dans la lecture de la position de la platine) il y aura violation de partage de la ressource.
Pour pallier cet inconvénient, j’ai décidé de créer un mode de priorité pour la tâche de fond, que j’ai appelé RealTime. Dans ce mode que l’on débute par un BeginRealTimeSession, la valeur retournée par GetRealTimePosition correspond à la réponse de la platine alors que GetPosition renvoie la dernière valeur retournée par GetRealTimePosition. Ainsi, et jusqu’à l’appel de EndRealTimeSession, la télécommande affiche les valeurs intérogées par le processus de recherche de franges. Pour que le processus fonctionne, j’ai fait l’a priori, qui consiste à dire que GetRealTimePosition est appelée très souvent. A priori tout à fait valable puisque la fonction située dans la fonction callback du timer, est appelée environ toutes les millisecondes.
Windows, un système fragile
La DAS1601, est à l’origine des ‘plantages’ éventuels du système. En effet, lors du portage sous Windows des librairies de la carte, les programmeurs de Keithley n’ont même pas tenu compte des spécificités de la gestion mémoire de Windows. Au résultat, la carte force ses allocations dynamiques dans la mémoire conventionnelle. Cette allocation brutale dans certain cas de figure peut avoir de très fâcheuses conséquences : Windows n’étant absolûment pas un système protégé, il arrive parfois qu’au démarrage, au moment de récupérer un handle sur la carte, on retourne directement sous DOS avec ou sans blocage de la machine. Les ingénieurs de chez Keithley conscients du problème nous ont conseillé de travailler sur une nouvelle version du gestionnaire.