LabWindows/CVI

cancel
Showing results for 
Search instead for 
Did you mean: 

Timers Multithread sous CVI

Bonjour,

 

J'ai plusieurs questions concernant mon programme avec LabWindows CVI 9.0 et 4 cartes NI PCI 6229, sous Windows XP et dual core Intel 3GHz.

 

Je souhaite réaliser un programme qui réalise une boucle d’asservissement et également un contrôle et affichage sur des acquisitions. Je n’ai malheureusement pas le choix sur l’OS et je ne peux pas m’orienter sur une solution temps réel.

 

Pour cela, je souhaite réaliser

-          une boucle qui gère l’asservissement (acquisition sur 20 entrées, calcul sur ces acquisitions, et génération sur 10 sorties, tout ceci en 10ms, c’est la BOUCLE CRITIQUE)

-          une boucle pour le contrôle (simple contrôle sur ces acquisitions: comparaison à une valeur seuil, 10ms)

-          une boucle pour l’affichage (affichage des résultats sur la face avant des contrôles OK ou non OK, toutes les 200ms)

 

Je souhaitais à la base utiliser 3 timers asynchrones de CVI, mais je me suis aperçu qu’on ne pouvait utiliser qu’un seul Timer asynchrone à la fois.

J’ai donc vu qu’il y avait une autre solution, le multithread en utilisant les thread pool. Cette solution m’intéresse, car je souhaiterai mettre la boucle critique dans 1 thread sur le 1er coeur, la boucle de contrôle dans un 2ème thread sur le 2ème coeur, et la boucle d’affichage dans un 3ème thread sur le 2ème coeur.

 

La boucle d’asservissement est la boucle critique et doit être de 10ms à +/- 2ms.

Les autres bouclent ne sont pas critiques et peuvent avoir un décalage de +/- 10ms, le but est de ne pas perdre de données et de contrôler toutes les acquisitions. Un affichage toute les 200ms suffit.

 

Voilà j’ai alors 2 questions :

 

-          est-ce que mon approche (timers en multithread et diviser sur les 2 cœurs) convient pour mon application ?

 

-          et comment mettre un timer classique dans un thread spécifique ? J’ai vu qu’il fallait utiliser «CmtScheduleThreadPoolFunction (int Pool_Handle, ThreadFunctionPtr Thread_Function, void *Thread_Function_Data, int *Thread_Function_ID)” Mais cette fonction appelle une fonction de type “CVICALLBACK FonctionThread(void *functionData)” et mon timer classique est de type “CVICALLBACK Timer_Classique (int panel, int control, int event, void *callbackData, int eventData1, int eventData2)” Comment donc appeler un timer classique avec une fonction CmtScheduleThreadPoolFunction ? Faut-il procéder d’une autre manière ?

 

 

Je vous remercie de votre aide.

 

Cordialement,

 

Hédi.

0 Kudos
Message 1 of 4
(3,546 Views)

Bonjour,

 

L'approche multi-thread est en effet la bonne, dès qu'il faut séparer plusieurs tâches. Je vous invite à consulter cet article du Rebel Site, qui traite du multithread et constitue une référence en la matière. Attention, le multithread n'implique pas obligatoirement une gestion multicoeur. Il faut pour le mettre en oeuvre passer par l'API de Windows.

 

Est-il bien nécessaire de faire de du contrôle 10ms? (deuxième boucle) Au quel cas, je ne suis pas absolument sûr qu'il faille créer un thread séparé. Si cette tâche peut supporter un cadencement plus lent, il serait plus malin de venir la traiter dans le troisième thread.

 

Il serait intéressant d'utiliser une Thread Safe Queue pour faire circuler les données d'un thread à l'autre. Ce mécanisme permettra de ne pas perdre de données. D'autre part, vous pouvez alors installer une callback sur un nombre d'éléments dans la TSQ (Thread Safe Queue) et venir déclencher la callback associée à cette TSQ dès que 20 éléments sont présents pour piloter le troisième thread depuis le premier.

 

Dans cette configuration, il n'y a plus besoin que d'un timer, pour la partie temps critique - bien que sous Windows, on ne puisse pas garantir le déterminisme d'une boucle à 10ms - les autres thread étant 'esclave' du premier.

 

Cordialement,

0 Kudos
Message 2 of 4
(3,508 Views)

Merci de votre réponse.

 

J'ai utilisé un timer asynchrone cadencé à 10ms et une Thread Safe Queue pour bufferiser les données. Cela fonctionne très bien, j'ai bien une boucle à 10ms à +/- 1ms et toutes mes acquisitions sont bien vérifiées.

 

Cordialement,

 

Hédi.

0 Kudos
Message 3 of 4
(3,483 Views)

J'ai parlé peut être un peu trop vite car je rencontre à nouveau un problème.

En effet, j'ai une erreur de type:

FATAL RUN-TIME ERROR:   Unknown source position, thread id 0x00000108:   The program has caused a 'General Protection' fault at 001B:00000005.

C'est la fonction "CmtReadTSQData" qui me génère cette erreur, du moins j'ai l'impression. En fait le programme tourne bien en mode Debug, mais en mode Release cette erreur apparait. Lorsque je n'appelle pas la fonction "CmtReadTSQData", l'erreur n'apparaît plus.

 

J'ai pourtant bien déclaré et paramétré ma TSQ, j'écrit dans ma TSQ dans mon timer asynchrone qui tourne toutes les 10ms, et je lis dans ma TSQ dans le thread principal de l'application, dans une boucle qui tourne toutes les 200ms. Lorsque je lis ma TSQ sur un évènement (par exemple un évènement lorsque que x données sont présentes dans la TSQ), j'ai toujours la même erreur qui apparaît en mode Release.

 

Auriez-vous une idée de la provenance de cette erreur?

 

Cordialement.

 

Hédi.

0 Kudos
Message 4 of 4
(3,438 Views)