le 10-07-2016 05:15 PM
Bonjour,
Je cherche à configurer des cartes NI et à les utiliser en C ; j'ai besoin de les synchroniser ; ça semble facile mais je patauge un peu...
Ma configuration :
- un chassis cDAQ-9178
- une carte NI 9265 (Analog Output Current) avec une sortie utilisée : je dois appliquer un courant
- une carte NI 9220 (Analog Input Voltage) avec deux entrées utilisées : une qui me donne un "trigger" et l'autre sur laquelle je dois lire une tension
Le Courant appliqué a une influence sur le Voltage lu, et c'est ça que je cherche à mesurer.
Je veux donc utiliser une horloge commune aux deux cartes pour appliquer le Courant (front montant) puis lire la Tension (front descendant). Toutes les secondes, j'ai un "top départ" : je dois lire 800 échantillons toutes les miliseconde.
Je veux donc :
- attendre le Trigger
- 800 fois (800 ms)
- appliquer une valeur de Output Current
- lire la tension conséquente
- récupérer les données
- attendre le TOP suivant (environ 200 ms plus tard) et recommencer
Je n'arrive pas à gérer l'horloge commune et le Trigger.
Pourriez-vous regarder mon code -et les commentaires qui posent des questions- et me donner un coup de main ?
Merci ++ !
TaskHandle taskOutput = 0; TaskHandle taskInput = 0; char trigName[256]; char timebaseSrc[256]; char errBuff[2048] = { '\0' }; float64 waveform[800]; for (int i = 0; i < 800; i++) waveform[i] = 0.01 * i / 800; /*********************************************/ // Sortie : on prépare l'envoi du Courant /*********************************************/ DAQmxErrChk(DAQmxCreateTask("Courant sortie", &taskOutput)); DAQmxErrChk(DAQmxCreateAOCurrentChan(taskOutput, "/cDAQ1Mod2/ao1", "Intensite", 0.0, 0.02, DAQmx_Val_Amps, NULL)); DAQmxErrChk(DAQmxCfgSampClkTiming(taskOutput, "", 1000., DAQmx_Val_Rising, DAQmx_Val_FiniteSamps, 800)); // la fonction suivante pose problème. Je veux dire "il y aura des triggers successifs". Est-ce nécessaire ? // DAQmxErrChk(DAQmxSetStartTrigRetriggerable(taskOutput, 1)); DAQmxErrChk(DAQmxWriteAnalogF64(taskOutput, 800, 0, 10.0, DAQmx_Val_GroupByChannel, waveform, NULL, NULL)); /*********************************************/ // Entrée : deux canaux /*********************************************/ DAQmxErrChk(DAQmxCreateTask("Tensions en entrée", &taskInput)); DAQmxErrChk(DAQmxCreateAIVoltageChan(taskInput, "/cDAQ1Mod1/ai0", "Tension_à_lire", DAQmx_Val_Cfg_Default, -10.0, 10.0, DAQmx_Val_Volts, NULL)); // les deux lignes suivantes pourraient définir la Tension qui va me servir de Trigger. Quelle ligne utiliser ? Et ça ne marche pas... // j'ai essayé, pendant un temps, de faire cette lecture de trigger dans une Task différente, mais ça n'a pas mieux fonctionné. // DAQmxErrChk(DAQmxCreateAIVoltageChan(taskInput, "/cDAQ1Mod1/ai1", "Tension_TRIGGER", DAQmx_Val_Cfg_Default, -10.0, 10.0, DAQmx_Val_Volts, NULL)); DAQmxErrChk(DAQmxCfgDigEdgeStartTrig(taskInput, "/cDAQ1Mod1/ai1", DAQmx_Val_Rising)); // la fonction suivante pose problème. Je veux dire "il y aura des triggers successifs". Est-ce nécessaire ? // DAQmxErrChk(DAQmxSetStartTrigRetriggerable(taskInput, 1)); DAQmxErrChk(DAQmxCfgSampClkTiming(taskInput, "", 1.0 * 1000, DAQmx_Val_Falling, DAQmx_Val_FiniteSamps, 800)); /*********************************************/ // TRIGGER pour lancer le top départ : j'essaye de le récupérer d'une carte pour l'appliquer à l'autre. /*********************************************/ DAQmxErrChk(GetTerminalNameWithDevPrefix(taskInput, "ai/StartTrigger", trigName)); // cette ligne est censée aligner les Triggers des cartes pour déclancher au même moment les écritures et lectures DAQmxErrChk(DAQmxCfgDigEdgeStartTrig(taskOutput, "cDAQ1/ai/StartTrigger", DAQmx_Val_Rising)); // je cherche ici à répliquer l'horlge de Input sur Output. Est-ce nécessaire ? Ca ne marche pas... DAQmxErrChk(DAQmxSetSampClkTimebaseSrc(taskOutput, "cDAQ1/ai/SampleClockTimebase")); // J'ai aussi essayé de jouer avec l'export de signal. Sans succès. //DAQmxErrChk(DAQmxExportSignal(taskInput, DAQmx_Val_StartTrigger, "ao/StartTrigger")); // récupérer les données est facile. Je ne la détaille pas. DAQmxErrChk(DAQmxRegisterEveryNSamplesEvent(taskInput, DAQmx_Val_Acquired_Into_Buffer, 800, 0, EveryFiniteCallback, NULL)); DAQmxErrChk(DAQmxRegisterDoneEvent(taskOutput, 0, DoneCallback, NULL));
le 10-08-2016 06:40 AM
La nuit porte conseil ; la lecture de documentations "autour" du sujet aussi... Je me réponds (si ça peut être utile à quelqu'un...)
En gros, si j'ai bien compris, les triggers et les horloges peuvent être pris sur le chassis, auquel cas ils sont communs aux cartes !
TaskHandle taskOutput = 0; TaskHandle taskInput = 0; float64 waveform[800]; for (int i = 0; i < 800; i++) waveform[i] = 0.01 * i / 800; /*********************************************/ // Sortie : on prépare l'envoi du Courant /*********************************************/ DAQmxErrChk(DAQmxCreateTask("Courant sortie", &taskOutput)); DAQmxErrChk(DAQmxCreateAOCurrentChan(taskOutput, "/cDAQ1Mod2/ao1", "Intensite", 0.0, 0.02, DAQmx_Val_Amps, NULL)); ///// Ici, on met une horloge à 80 MHz du chassis DAQmxErrChk(DAQmxCfgSampClkTiming(taskOutput, "/cDAQ1/80MhzTimebase", 1000., DAQmx_Val_Rising, DAQmx_Val_FiniteSamps, 800)); DAQmxErrChk(DAQmxWriteAnalogF64(taskOutput, 800, 0, 10.0, DAQmx_Val_GroupByChannel, waveform, NULL, NULL)); /*********************************************/ // Entrée : un seul canal /*********************************************/ DAQmxErrChk(DAQmxCreateTask("Tensions en entrée", &taskInput)); DAQmxErrChk(DAQmxCreateAIVoltageChan(taskInput, "/cDAQ1Mod1/ai0", "Tension_à_lire", DAQmx_Val_Cfg_Default, -10.0, 10.0, DAQmx_Val_Volts, NULL)); ///// Même horloge que précédemment DAQmxErrChk(DAQmxCfgSampClkTiming(taskInput, "/cDAQ1/80MhzTimebase", 1.0 * 1000, DAQmx_Val_Falling, DAQmx_Val_FiniteSamps, 800)); /*********************************************/ // TRIGGER pour lancer le top départ : le prendre sur le chassis /*********************************************/ DAQmxErrChk(DAQmxCfgDigEdgeStartTrig(taskOutput, "/cDAQ1/PFI0", DAQmx_Val_Rising)); DAQmxErrChk(DAQmxCfgDigEdgeStartTrig(taskInput, "/cDAQ1/PFI0", DAQmx_Val_Rising)); // récupérer les données est facile. Je ne la détaille pas. DAQmxErrChk(DAQmxRegisterEveryNSamplesEvent(taskInput, DAQmx_Val_Acquired_Into_Buffer, 800, 0, EveryFiniteCallback, NULL)); DAQmxErrChk(DAQmxRegisterDoneEvent(taskOutput, 0, DoneCallback, NULL));