Création d'un driver de caméra

Télécharger l'ensemble des fichiers (libcam.zip (230 Ko)).

1. Présentation

Le logiciel AudeLA contient quelques pilotes de caméras (Audine, Hisis-11, Hisis22, ST7). Grâce aux extensions Tcl, il est possible de programmer n'importe quel nouveau pilote. Pour illustrer cette documentation, nous allons reprogrammer le driver Audine dans le but d'y apporter des modifications personnelles. Ce pilote sera appelé "audineperso" afin de le distinguer du pilote déjà existant dans AudeLA. Ainsi, d'un point de vue pratique, pour l'utilisateur de AudeLA, l'emploi du nouveau driver consistera tout simplement à créer un objet caméra de type audineperso :

::cam::create audineperso lpt1

Cette fonction va appeler votre pilote, qui va se présenter sous la forme d'un fichier de librairie (.dll ou .so). Il va fournir des fonctions à l'objet caméra crée par AudeLA. Votre pilote contiendra au moins la fonction "acq" afin de lancer une acquisition d'image. Pour l'utilisateur, la fonction d'acquisition se présentera classiquement par :

cam1 acq

Ainsi, votre pilote est entièrement compatible avec tous les scripts AudeLA. Le choix de la caméra n'intervient qu'une seule fois au moment de la création de l'objet caméra (::cam::create, vu précédemment). Enfin, le fichier du driver sera copié dans le dossier binwin (pour la DLL Windows) ou binlinux (pour le SO Linux) et portera le nom du pilote précédé de "lib". Ainsi, le pilote "audineperso" sera le fichier libaudineperso.dll sous Windows.

Afin d'assurer la compatibilité entre les objets caméra de AudeLA et vos fonctions de pilotage, il convient d'appliquer quelques règles de programmation. Nous avons simplifier ces règles en fournissant un canevas de programmation sur lequel peu de modifications sont nécessaires pour le modifier à vos besoins.

Ce kit de programmation permet donc d'écrire un pilote (driver) pour une caméra, de telle sorte qu'elle puisse être utilisée de la même manière que celles présentes à l'origine dans AudeLA, sous Linux et sous Windows.
 

2. Fonctionnement

La création d'une caméra dans AudeLA s'effectue grace à la fonction "cam::create type port". Les types de caméras gérés en standard par AudeLA sont : audine, hisis11, hisis22-12, hisis22-14, st7. Si on entre un type inexistant, alors AudeLA va chercher à charger le driver correspondant dans le répertoire du programme (binwin ou binlinux suivant les plateformes). Le driver doit avoir un nom bien précis pour qu'AudeLA fasse le lien avec le type entré dans la commande cam::create : par exemple, si on entre "cam::create audineperso lpt1" alors AudeLA essaiera de charger le fichier libaudineperso.dll dans son répertoire d'installation. Par ce mécanisme, il suffit simplement que le driver soit présent pour qu'AudeLA puisse l'utiliser : pas besoin d'installation particulière, un copier/coller suffit.

Ces drivers sont en réalité des extensions Tcl avec tout le formalisme inhérent, qui n'exportent qu'une seule fonction (dont le nom est celui de la caméra), dont deux arguments doivent lui être fourni (le nom de la commande qui est associée à la caméra créée : cam5 par exemple, puis le port). L'appel à cette fonction permet de créer effectivement la fonction caméra, et elle devient disponible par la suite.

L'exemple suivant montre ce qu'il se produit avec une caméra xyz.

cam::create xyz lpt2
AudeLA essaie de charger l'extension Tcl libxyz.dll : si le chargement est réussi, la librarie ajoute une commande Tcl appellée xyz qui sert à créer des objets caméra. AudeLA appelle donc cette commande, avec le numéro de caméra qui a été déterminé, et le port passé à la commande cam::create. C'est-à-dire "xyz cam1 lpt2". Le driver s'occupe alors de créer l'objet caméra cam1, qui devient dès-lors accessible.
cam1 exptime 5
cam1 bin {2 2}
cam1 acq
Ces commandes appellent les divers sous-commandes implémentées dans le driver de la caméra xyz.
 

3. Organisation

Le kit d'écriture d'un driver de caméra autochargeable (plug-in) est constitué des fichiers suivants : De plus, il faut remplacer votre fichier libstd.dll par la nouvelle version présente dans le fichier libcam.zip (le source n'est pas encore disponible). La nouvelle libstd.so arrivera bientôt.

Quelques fichiers additionnels sont fournis,

Les fichiers de projet fonctionnent de manière optimale sous Windows dans la mesure où l'arborecence des dossiers est la suivante :

c:\audela\dev\libcam

Note : Lors de la compilation avec Visual C++, il est normal d'avoir le message d'alerte suivant :
C:\audela\dev\libcam\src\util.c(82) : warning C4035: 'libcam_in' : no return value
Ceci est dû au fait que la valeur de retour est écrite en assembleur et donc le compilateur ne peut pas la voir directement.

4. Ecriture du driver

Les fichiers camera.c et camera.h sont ceux qu'il faut modifier pour les rendres spécifiques à telle ou telle caméra (nom du driver, fonctions de lecture, etc.). Tout ce qui suit, repose sur la programmation du pilote personnalisé de la caméra audine. Ce pilote est appelé "audineperso". Avant d'analyser en détail comment modifier les fichiers camera.h et camera.c, nous allons explorer le contenu du fichier libcam.c qui constitue le lien entre Tcl et le C.

4.1. libcam.c

Ce fichier contient tout le nécessaire pour "connecter" le driver à AudeLA et pour apporter des fonctions à l'objet caméra de AudeLA. Il n'y a pas besoin de modifier son contenu pour créer un nouveau driver mais nous allons, grâce à un petit exemple, anbalyser son mode de fonctionnement. On trouvera notamment la définition de la structure "cmditem" contenant la liste des commandes disponibles pour le driver.

static struct cmditem cmdlist[] = {
   {"acq", cmdCamAcq},
   {"bin", cmdCamBin},
   {"buf", cmdCamBuf},
   {"exptime", cmdCamExptime},
   {"info", cmdCamInfo},
   {"stop", cmdCamStop},
   {"tel", cmdCamTel},
   {"window", cmdCamWindow},
   {NULL, NULL}
};

Si l'on souhaite ajouter une fonction, par exemple "shutter" (pour indiquer si l'on souhaite ou non faire fonctionner un obturateur), on ajoutera la ligne suivante dans la structure cmditem :

   {"shutter", cmdCamShutter},

Le mot cmdCamShutter est le nom de la fonction C qui va faire le lien avec le Tcl. Il faut donc créer la fonction cmdCamShutter dans le fichier libcam.c. On commencera par déclarer cette fonction :

int cmdCamShutter(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[]);

La liste des arguments est toujours celle-ci, quelque soit votre fonction. Elle correspond au prototype d'une fonction C qui peut être enregistrée dans l'interpréteur Tcl.

Il ne reste plus qu'à créer la fonction par elle-même :

int cmdCamShutter(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[])
{
   int retour = TCL_OK;
   char ligne[256];
   double d_exptime;
   struct camprop *cam;
   if((argc!=2)&&(argc!=3)) {
      /* --- pas le bon nombre d'arguments pour cette fonction ---*/
      sprintf(ligne,"Usage: %s %s ?on/off?",argv[0],argv[1]);
      Tcl_SetResult(interp,ligne,TCL_VOLATILE);
      retour = TCL_ERROR;
   } else if(argc==2) {
       /* --- la fonction est sans argument. On retourne la valeur ---*/
      cam = (struct camprop*)clientData;
      sprintf(ligne,"%s",cam->shutter);
      Tcl_SetResult(interp,ligne,TCL_VOLATILE);
   } else {
      if ((strcmp(argv[2],"on")==0)||(strcmp(argv[2],"off")==0)) {
         /* --- on stocke la nouvelle valeur dans la structure camprop ---*/
         cam = (struct camprop*)clientData;
         strcpy(cam->shutter,argv[2]);
         sprintf(ligne,"%s",cam->shutter);
         Tcl_SetResult(interp,ligne,TCL_VOLATILE);
      } else {
         /* --- l'argument n'est pas valable ---*/
         sprintf(ligne,"Usage: %s %s ?on/off?",argv[0],argv[1]);
         Tcl_SetResult(interp,ligne,TCL_VOLATILE);
         retour = TCL_ERROR;
      }
   }
   return retour;
}

La structure de type "camprop" doit contenir l'élément "shutter". Ceci sera réalisé dans le fichier camera.h, exposé plus loin.

L'enregistrement de la fonction cmdCamShutter et de son nom d'appel (shutter) dans l'interpréteur Tcl, se fait automatiquement dans le fichier libcam.c lors de la création de la nouvelle caméra. Pour comprendre en détail ce point, consulter le paragraphe concernant la programmation avancée avec libcam.c.

4.2. camera.h

Dans camera.h sont définis un certain nombre de #defines, qui permettent de configurer l'interface avec Tcl :

#define CAM_NAME "audineperso"

Nom de la commande Tcl générique permettant de créer une commande de pilotage de la caméra. A chaque fois que le compilateur rencontrara la chaîne CAM_NAME dans le source des fichiers de votre librairie, cette chaîne sera rempléce par sont contenu, c'est à dire : audineperso.
#define CAM_ENTRYPOINT Audineperso_Init
Nom du point d'entrée de la librairie. La convention de Tcl en la matière est qu'il corresponde à Xx_Init pour une extension s'appellant libxx.dll ou libxx.so par exemple.
Pour le driver libkitty.dll : #define CAM_ENTRYPOINT Kitty_Init
Pour le driver libaudineperso.dll : #define CAM_ENTRYPOINT Audineperso_Init
Pour les environnement de compilation où le fichier .def est nécessaire, ne pas oublier de mettre à jour manuellement le champ EXPORT
#define CAM_LIBNAME "libaudineperso"
#define CAM_LIBVER "1.0"
Ces deux variables permettent d'identifier le driver auprès de Tcl, via la commande "package names". Normalement, le fichier libaudineperso n'est pas appelé par AudeLA par la commande "package require" mais par un simple "load" lors de la fonction ::cam:create.
#define CAM_DESCR "AudeLA "CAM_NAME" driver"
Description renvoyée par la commande cam1 info. Ne pas changer le mot CAM_NAME car il est défini plus haut (égal à "audineperso" dans notre exemple) et sera donc remplacé automatiquement par sa valeur lors de la compilation.
#define PHOTOX 9e-6
#define PHOTOY 9e-6
#define MAXX 768
#define MAXY 512
Définition de la dimension des photoélements (en mètre) et du nombre de lignes et de colonnes actives dans la matrice (ici un Kaf-400 dans notre exemple).
La structure "camprop" permet de rassembler l'ensemble des variables du driver à partager entre les fonctions de pilotage.

struct camprop {
   /* --- pour le CCD Kaf-400 ---*/
   int nb_photox;
   int nb_photoy;
   int nb_deadbeginphotox;
   int nb_deadendphotox;
   int nb_deadbeginphotoy;
   int nb_deadendphotoy;
   double size_photox;
   double size_photoy;
   double fill_factor;
   /* --- parametres standards ---*/
   float exptime;
   int binx, biny;
   int x1, y1, x2, y2;
   int w, h;
   int bufno;
   int camno;
   int telno;
   unsigned short port;
   /* Ces deux variables sont utilisees en interne par libcam. Ne pas modifier */
   struct TimerExpirationStruct *timerExpiration;
   struct camprop *next;
};

Dans le cas de l'utilisation d'un obturateur, on pourra ajouter l'élément shutter pour indiquer si on utilise ou non ce périphérique. Dans ce cas, on ajoutera alors l'élément char shutter[4]; dans la structure.

La déclaration des "macro" fonctions de pilotage de la camera :

extern int cam_init(struct camprop *cam, char*port);
extern char* cam_errmsg(int err);
extern void cam_update_window(struct camprop *cam);
extern void cam_start_exp(struct camprop *cam);
extern void cam_stop_exp(struct camprop *cam);
extern void cam_read_ccd(struct camprop *cam, short *p);

Ces fonctions sont relativement communes à chaque caméra. Elle correspondent à l'enchaînement des séquences de base pour définir la fenêtre d'acquisition, démarrer et arrêter une pose, lire le CCD et stocker la valeur des pixels en mémoire, etc. Ces fonctions sont directement appelées par les fonctions du fichier "libcam.c". C'est la raison pour laquelle il ne faut pas changer leur nom mais on pourra modifier éventuellement leur contenu pour ajouter une fonctionnalité.
La déclaration des fonctions basiques de pilotage de la caméra :

void fast_vidage_inv(struct camprop *cam);
void zi_zh_inv(struct camprop *cam);
void read_pel_fast_inv(struct camprop *cam);
void read_win_inv(struct camprop *cam,short *buf);
void fast_line_inv(struct camprop *cam);

Ces fonctions constituent véritablement le coeur du driver. Elles passent toutes la structure "camprop" qui contient tous les paramétrages de l'acquisition CCD à effectuer (la fenêtre, le temps de pose, le binning, etc.). Leur nom et leur contenu sera très différent d'un driver à l'autre. Ces fonctions sont appelées par les "macros" définies précédemment.

4.3. Camera.c

Les fonctions propres au driver se trouvent dans le fichier camera.c et peuvent être sépraées en deux catégories : les macros, appelées par les fonctions du fichier libcam.c, et les fonctions basiques de communication entre la caméra et le logiciel. Aucune fonction du fichier camera.c ne fait appel à l'interpréteur Tcl.

4.3.1. Les "macro" fonctions de pilotage de la caméra

La fonction cam_init est appelée à la création de la commande caméra (::cam::create) pour initialiser les diverses variables et propriétés de la caméra. Cette fonction initialise donc les éléments de la fameuse structure "camprop":

int cam_init(struct camprop *cam, char *port) {
   /* --- initialisation de la fenetre ---*/
   cam->x1 = 1;
   cam->y1 = 1;
   cam->x2 = MAXX-1;
   cam->y2 = MAXY-1;
   /* --- initialisation du mode de binning ---*/
   cam->binx = 2;
   cam->biny = 2;
   cam_update_window(cam);
   /* --- initialisation du temps de pose ---*/
   cam->exptime = (float)0.;
   /* --- initialisation du numero de port ---*/
   if(strcmp(port,"lpt1")==0) {
      cam->port = 0x378;
   } else if(strcmp(port,"lpt2")==0) {
      cam->port = 0x278;
   } else {
      return 1;
   }
   /* --- pour audine parametres fixes pour la lecture ---*/
   cam->nb_photox = MAXX;
   cam->nb_photoy = MAXY;
   cam->size_photox = PHOTOX;
   cam->size_photoy = PHOTOY;
   cam->fill_factor = 1.;
   cam->nb_deadbeginphotox = 14;
   cam->nb_deadendphotox = 14;
   cam->nb_deadbeginphotoy = 4;
   cam->nb_deadendphotoy = 4;
   return 0;
}

Quelques autres fonctions utilitaires :

char* cam_errmsg(int errno)

cam_errmsg sert à transformer un numéro d'erreur en une chaîne de caractères compréhensible.
void cam_update_window(struct camprop *cam)
cam_update_window est appellée à chaque modification de la fenêtre (sous-commande window) ou du binning (sous-commande bin) afin de vérifier / adapter les valeurs fournies. L'exemple donné dans le kit permet de recadrer la fenêtre d'acquisition par rapport aux dimensions du CCD, de calculer la largeur et hauteur de l'image en pixels à partir des coordonnées de la fenêtre et du binning, et enfin de mettre à jour les coordonnées du coin supérieur droit en fonction du binning. Cela sert par exemple à autoriser ou non une fenêtre en binning 3x3 à commencer sur des pixels d'indice non multiple du binning : les st7 peuvent faire du binning 3x3 commencant aux pixels 1, 4, 7, etc. alors que Audine peut faire du binning 3x3 en commencant n'importe où.
void cam_start_exp(struct camprop *cam)
C'est la commande appellée au début d'une prise de vue, pour faire un vidage du CCD (cas de Audine), ou bien envoyer les paramètres de pose à la caméra (st7). Dans le cas du driver "audineperso", voici le contnue la cette fonction :

void cam_start_exp(struct camprop *cam)
{
   short i;
   short nb_vidages = 4;
   /* Bloquage des interruptions */
   libcam_bloquer();
   /* vidage de la matrice */
   for (i=0;i<nb_vidages;i++) fast_vidage_inv(cam);
  /* Debloquage des interruptions */
   libcam_debloquer();
   /* Remise a l'heure de l'horloge de Windows */
   update_clock();
}

La fonction libcam_bloquer sert à stopper les interruptions afin d'assurer une bonne synchronisation des ordres envoyés sur le port parallèle. La fonction libcam_bloquer est définie dans util.c. On appelle ensuite le fonction basique de vidange du CCD : fast_vidage_inv. Cette opération est réalisée 4 fois. pour changer ce nombre, on modifiera la valeur de la variable nb_vidages.

La fin de la fonction consiste à débloquer les intérruptions et à remettre l'horloge du système d'exploitation à l'heure par rapport au BIOS. Les fonctions libcam_debloquer et update_clock sont définies dans le fichier util.c.

void cam_stop_exp(struct camprop *cam)
Cette fonction est appellée quand une pose est interrompue, soit à la fin "naturelle" d'une pose, pour fermer un obturateur par exemple.
void cam_read_ccd(struct camprop *cam, short *p)
Fonction de lecture du CCD, le pointeur sur les pixels est p, il est préalablement dimensionné à la taille des images par la fonction d'appel de libcam.c. Le contenu de cette fonction reste simple :

void cam_read_ccd(struct camprop *cam, short *p)
{
   int w = cam->w;
   int h = cam->h;
   if(p==NULL) return ;
   /* Bloquage des interruptions */
   libcam_bloquer();
   /* Lecture de l'image */
   read_win_inv(cam,p);
   /* Debloquage des interruptions */
   libcam_debloquer();
   /* Remise a l'heure de l'horloge de Windows */
   update_clock();
}

La fonction "basique" de lecture read_win_inv constitue le coeur de driver de la caméra.

4.3.2. Les fonctions "basiques" de pilotage de la caméra

ces fonctions sont celles décrites par la documentation de la caméra Audine pour écrire le driver. Nous les avons simplement adapté pour qu'elles puissent décoder les variables contenues dans la structure "camprop".

fast_vidage_inv(struct camprop *cam)

Vidage rapide de la matrice. Le decalage des lignes s'effectue ici par groupe de 4, mais est le seul parametre a regler ici.
read_win_inv(struct camprop *cam,short *buf)
Lecture normale du CCD, avec un fenêtrage possible. C'est la fonction clé du driver :

void read_win_inv(struct camprop *cam,short *buf)
{
   int i,j;
   int k,l;
   int imax,jmax;
   int cx1,cx2,cy1;
   unsigned short int port0, port1;
   short buffer[2048];
   short *p0;
   int x;
   int a1,a2,a3,a4;
   p0=buf;
   port0 = cam->port;
   port1 = port0+1;
   /* Calcul des coordonnees de la    */
   /* fenetre, et du nombre de pixels */
   imax = (cam->x2-cam->x1+1)/cam->binx;
   jmax = (cam->y2-cam->y1+1)/cam->biny;
   cx1 = cam->nb_deadbeginphotox+(cam->x1-1);
   cx2 = cam->nb_photox-cam->x2+cam->nb_deadendphotox;
   cy1 = cam->nb_deadbeginphotoy+(cam->y1-1);
   /* On supprime les cy1 premieres lignes */
   for(i=0;i<cy1;i++) {
      zi_zh_inv(cam);
      fast_line_inv(cam);
      fast_line_inv(cam);
   }
   /* boucle sur l'horloge verticale (transfert) */
   for (i=0;i<jmax;i++) {
      /* Nettoyage du registre horizontal */
      fast_line_inv(cam);
      /* Cumul des lignes (binning y) */
      for(k=0;k<cam->biny;k++) zi_zh_inv(cam);
      /* On retire les cx1 premiers pixels */
      for (j=0;j<cx1;j++) read_pel_fast_inv(cam);
      /* boucle sur l'horloge horizontale (registre de sortie) */
      for(j=0;j<imax;j++) {
         libcam_out(port0,247); /* reset 11110111 */
         libcam_out(port0,255); /* délai critique 11111111 */
         libcam_out(port0,255);
         libcam_out(port0,255);
         libcam_out(port0,255);
         libcam_out(port0,255);
         libcam_out(port0,239); /* clamp 11101111 */
         libcam_out(port0,255);
         libcam_out(port0,239);
         for(l=0;l<cam->binx;l++) {
            libcam_out(port0,255);
            libcam_out(port0,251); /* palier vidéo 11111011 */
         }
         libcam_out(port0,251);
         libcam_out(port0,251);
         libcam_out(port0,251);
         libcam_out(port0,219); /* start convert 11011011 */
         libcam_out(port0,219);
         libcam_out(port0,219);
         libcam_out(port0,219);
         libcam_out(port0,219);
         /* numerisation */
         a1 = libcam_in(port1) & 0x00F0;
         libcam_out(port0,91);
         a2 = libcam_in(port1) & 0x00F0;
         libcam_out(port0,155);
         a3 = libcam_in(port1) & 0x00F0;
         libcam_out(port0,27);
         a4 = libcam_in(port1) & 0x00F0;
         x = ((a1>>4)+a2+(a3<<4)+(a4<<8))^0x8888;
         if (x>32767) x=32767;
         /* Stockage dans un buffer dans la meme page mem */
         buffer[j] = (short)x;
      }
      /* On retire cx2 pixels à la fin */
      for (j=0;j<cx2;j++) read_pel_fast_inv(cam);
      /*
      Ca c'est pour stocker les pixels dans la memoire
      centrale. Notons que ca pose PB car il peut y avoir
      un swap memoire et ca fait sauter le cli. On ne fait
      le transfert qu'une fois par ligne, pour eviter les
      cochonneries.
      */
      for(j=0;j<imax;j++) {
         *(p0++)=buffer[j];
      }
   }
}

Cette fonction permet donc de transférer les pixels du CCD vers le pointeur de l'image temporaire en numérisant le signal. Le binning et le fenêtrage sont pris en compte.

zi_zh_inv(struct camprop *cam)
Décalage horizontal d'une ligne (dans le registre horizontal). Cette fonction est simple, en voilà son contenu :

void zi_zh_inv(struct camprop *cam) {
   unsigned short port = cam->port;
   int i;
   const int n_iter = 8;
   for(i=0;i<n_iter;i++) libcam_out(port,0xFB); /* 11111011 */
   for(i=0;i<n_iter;i++) libcam_out(port,0xFA); /* 11111010 */
   for(i=0;i<n_iter;i++) libcam_out(port,0xF9); /* 11111001 */
   for(i=0;i<n_iter;i++) libcam_out(port,0xFA); /* 11111010 */
   for(i=0;i<n_iter;i++) libcam_out(port,0xFB); /* 11111011 */
}

Il s'agit d'alterner les horloges V1 et V2 (pilotés par les deux bits de poids faible) pour décaler l'image d'une ligne vers le registre horizontal (cf. documentation Kodak). La durée du créneau est déterminée par la variable n_iter. Si l'on souhaite effectuer le décalage plus rapidement, on diminuera la valeur de n_iter. retenir qu'un appel à libcam_out dure environ 2 microsecondes.

read_pel_fast_inv(struct camprop *cam)
Lecture rapide d'un pixel : decalage du registre horizontal avec Reset, mais sans lecture du CAN,
fast_line_inv(struct camprop *cam)
Lecture rapide du registre horizontal, avec la fonction read_pel_fast_inv.

4.4. Util.c, les fonctions utilitaires

Des fonctions utilitaires sont disponibles dans le fichier util.c. Leur rôle est de fournir une interface indentique à des fonctions qui dépendent du système d'exploitation (notamment les E/S). Elles sont :

void libcam_swap(int *a, int *b)

Echange de deux entiers.
void libcam_out(unsigned short a, unsigned char d)
Sortie d'un octet sur un port donné.
unsigned char libcam_in(unsigned short a)
Lecture d'une donnée sur un port donné.
void libcam_bloquer()
Empeche les interruptions du processeur (utile pour la lecture du CCD).
void libcam_debloquer()
Rétablit les interruptions.

4.5. Synthèse du cheminenement dans le driver

Voici comment on chemine dans le driver dans le cas de la fonction cam1 acq :

4.6. Programmation avancée dans libcam.c

L'interface entre le driver et le langage Tcl est deja écrite, dans ses grandes lignes tout du moins, et ne nécessite a priori pas d'ajout. C'est le fichier libcam.c qui réalise cela, mais il est recommandé de connaître le mécanisme des extensions Tcl avant de se plonger dedans.

La fonction exportée CAM_DRIVERENTRY (=Cam_Init...) est la fonction appellée par le mécanisme de chargement d'extension de Tcl. Elle permet d'identifier le module, et d'enregistrer les fonctions Tcl propres au module : ici il n'y en a qu'une, il s'agit de CAM_NAME (#define dans camera.h), qui dans le code C s'appelle cmdCamCreate. L'appel à cette fonction permet de créer des nouvelles commandes Tcl faisant office d'objets caméra. Ces nouvelles commandes Tcl vont porter le nom passé en paramètre à la commande Tcl CAM_NAME, et appellent la fonction C cmdCam avec un clientData propre à chaque instance de caméra (ce qui permet de créer plusieurs caméras du même type). Cette fonction appelle ensuite la sous-fonction idoine en fonction de la sous-commande Tcl. Par exemple, "cam1 exptime 5" va appeller la fonction cmdCam avec le clientData propre à l'instance cam1, qui va elle-même appeller la fonction C cmdCamExptime . A noter que le clientData est de type void*, et son contenu est laissé à la discretion des programmeurs. Ici nous l'utilisons pour passer un pointeur sur la structure camprop (voir camera.h) propre à l'instance de la caméra, et créé lors de la créate de la caméra (dans cmdCamCreate).

La correspondance entre le nom de la sous-commande Tcl (bin, exptime, etc...) et la fonction C correspondante est définie par le tableau statique cmdlist défini au début de libcam.c. Pour ajouter une nouvelle sous-commande, ajouter un item dans ce tableau, et écrire la fonction C correspondante. Elle sera automatiquement prise en compte dans cmdCam et donc depuis AudeLA.

Les sous-commandes permettent de mettre à jour la structure camprop propre à chaque instance,
permet d'interfacer le driver à Tcl, en créant la commande d'interface, et les sous-commandes de gestion de la caméra (temps de pose, binning, etc...). Ces dernières fonctions mettent à jours les éléments d'une structure propre à chaque instance de caméra de type struct camprop, passée à chaque commande spécifique par le pointeur *cam.
 
 
 

Denis Marchais et Alain Klotz
05/01/2001