LA PROGRAMMATION


Un programme d'acquisition commandant la caméra Audine comportera
2 types de fonctions :


  • Des fonctions de haut niveau. Elles se chargent de contrôler les
    paramètres de l'acquisition, de gérer le temps d'intégration,
    de sauvegarder des données sur une mémoire de masse, d'afficher
    l'image, de piloter un télescope, ...
  •  

  • Des fonctions de bas niveau, appelées par les fonctions de haut
    niveau, dont le rôle est de dérouler le chronogramme de lecture
    du CCD et de réceptionner le résultat de la numérisation
    de chaque pixel.

La description des fonctions de haut niveau sort du cadre de cette documentation.
Le sujet est en effet trop vaste et le code trop dépendant de la
vision que l'on a d'un logiciel d'acquisition. Notons que le langage importe
peu pour ces fonctions, car la vitesse d'exécution n'est pas ici
la priorité.

En revanche, un soin particulier doit être apporté à
l'écriture des routines de bas niveau car la performance en vitesse
de lecture en dépend directement. Un bon compilateur est nécessaire,
voir même, l'écriture des routines en langage machine.


Le programme pisco est assez caractéristique.
L'interface est écrite en Visual Basic 6.0, un outil extrêmement
performant pour cela. Les fonctions de lecture du CCD sont en revanche
regroupées dans une librairie (une DLL) écrite en C (Visual
C++ 6.0).



Donc, les informations qui suivent sont destinées aux programmeurs
expérimentés qui souhaitent inclure le pilotage de la caméra
Audine dans leur logiciel.

Le tableau ci-après est un rappel de la fonction des 8 bits
du registre de données du port parallèle.






BIT


FONCTION
Bit 0 Horloge V1
Bit 1 Horloge V2
Bit 2 Horloges H1 et H2
Bit 3 Horloge R
Bit 4 Horloge de clamp
Bit 5 Début de conversion du CAN
Bit 6 Multiplexage au niveau du CAN 
Bit 7 Multiplexage au niveau du 74HCT157


Par ailleurs, les 4 bits de poids fort du registre d'état du
port parallèle sont exploités pour recevoir en 4 passes le
mot de 16 bits correspondant à la lecture d'un pixel.

La routine en langage C, READ_AUDINE, contient le code de lecture standard
de la caméra Audine en binning 1x1 (l'image numérique finale
a une taille de 768x512 points).


Les deux paramètres de READ_AUDINE sont l'adresse de base du
port parallèle, et un pointeur sur une zone mémoire allouée
suffisamment grande pour pouvoir contenir l'image (ici, il faudra 2 x 768
x 512 = 786432 octets au minimum, l'image étant codée sur
16 bits).


La variable P contient l'adresse du registre de données et la
variable P2 l'adresse du registre d'état.


Il faut commencer par éliminer les 4 premières lignes
de l'image CCD, car elles sont masquées de la lumière et
ont peu d'attrait en astronomie. Deux fonctions sont appelées (leur
listing sera donné plus loin).


La première, ZI_ZH, a pour charge de générer
la séquence d'horloge qui transfère
le contenu d'une ligne de la zone image dans le registre horizontal.
Une fois cette opération réalisée, la routine FAST_LINE
effectue une lecture rapide du registre horizontal (sans numérisation),
de manière à nettoyer ce registre avant qu'il ne reçoive
le contenu de la ligne suivante


for (i=0;i<4;i++)

   {

   zi_zh(P);

   fast_line(P);

   }


Les autres lignes sont numérisées, à concurrence
de 512 lignes :



for (i=0;i<imax;i++)


La première chose à faire est de nettoyer le registre
horizontal par une lecture rapide afin d'en retirer les charges thermiques
qui ont pu y apparaître, alors que la ligne précédente
était en cours de lecture. Ce n'est qu'ensuite que la ligne à
convertir est transférée dans le registre horizontal :


fast_line(P);

zi_zh(P);


La routine READ_PEL_FAST est appelée 14 fois. Cette séquence
d'instruction permet de lire rapidement (sans numérisation) les
14 premiers pixels d'une image, qui ne contiennent pas d'informations utiles
pour nous :


for (j=0;j<14;j++)

   {


   read_pel_fast(P);

   }


La numérisation proprement dite des pixels de la ligne courante
débute alors. Le couple d'instructions :


outp(P,247);

outp(P,255);


produit le top de reset. Rappelez-vous que les circuits d'interface
d'Audine inversent tous les bits de commande. Pour  signifier à
Audine que l'ensemble des bits sont à zéro, vous devez écrire
255 dans le registre de données. Dans le cas du top de reset, nous
écrivons la configuration binaire suivante dans le registre : 11110111,
soit 247 en décimal. Seul le bit 3 est mis à zéro
dans le registre, ce qui signifie qu'au niveau de la caméra, la
ligne correspondante passe à 1. Ce niveau retombe à 0 avec
l'instruction suivante, outp(P,255),

ce qui génère bien le top de reset.


Un délai d'attente est ménagé. Une instruction
OUTP
dure environ 1,7 microseconde, et ce de manière à peu près
constante quelle que soit la vitesse de l'ordinateur. Ce délai laisse
le temps au palier de référence de bien s'établir
:


outp(P,255);

outp(P,255);

outp(P,255);

outp(P,255);

outp(P,255);


Le top de clamp est alors envoyé :



outp(P,239); 
// clamp


outp(P,255);


Le palier vidéo s'établit ensuite, avec un délai
d'attente suffisant pour effectuer la numérisation sur un signal
bien stabilisé :


outp(P,251); 
// palier vidéo


outp(P,251);

outp(P,251);

outp(P,251);


La numérisation débute alors. Le délai correspond
au temps de conversion. On choisit 10 microsecondes, ce qui est correct
pour le CAN AD976 :


outp(P,219); 
// start convert


outp(P,219);

outp(P,219);

outp(P,219);

outp(P,219);


outp(P,219);


Les 4 nibbles sont lus avec les décalages de bits et les masquages
adéquats (rappelez-vous que le bit 7 du registre de statut donne
toujours une représentation inversée du signal qu'il reçoit).
Entre chaque lecture de nibble, une commande est envoyée vers les
circuits de multiplexage de la caméra.


// numérisation

a1=(inp(P2))>>4;

outp(P,91);

a2=(inp(P2))>>4;

outp(P,155);

a3=(inp(P2))>>4;


outp(P,27);

a4=(inp(P2))>>4;

x=(a1+(a2<<4)+(a3<<8)+(a4<<12))^0x8888;

if (x>32767) x=32767;

*(PTR--)=x;


La fonction READ_AUDINE permet de numériser
l'intégralité de la matrice KAF-0400 en 15 secondes sur la
plupart des PC. Voici son code complet :



/*****************
read_audine *****************/


/* Audine                                    
*/


/*=============================================*/

/* Lecture en binning
1x1                    
*/


/***********************************************/

void read_audine(short
base,short *buf)


{


short P,P2;

int x;

int a1,a2,a3,a4;

int i,j;

int imax,jmax;

short *PTR


imax=512;

jmax=768;


P=base;

P2=P+1;

PTR=buf;


/**** on retire les
4 premières lignes ****/


for (i=0;i<4;i++)

   {

   zi_zh(P);


   fast_line(P);

   }


for (i=0;i<imax;i++)

   {

   fast_line(P);

   zi_zh(P);


 

   /****
on retire les 14 premiers pixels ****/


   for
(j=0;j<14;j++)


    
{


    
read_pel_fast(P);


    
}



   p0=p0+imax-1;

   for
(j=0;j<jmax;j++)


    
{


    
outp(P,247);   // reset


    

outp(P,255);   // palier de référence


    
outp(P,255);


    
outp(P,255);


    
outp(P,255);


    
outp(P,255);


    

outp(P,239);   // clamp


    
outp(P,255);


    
outp(P,251);   // palier vidéo


    
outp(P,251);


    
outp(P,251);



    
outp(P,251);


    
outp(P,219);   // start convert


    
outp(P,219);


    
outp(P,219);


    
outp(P,219);



    
outp(P,219);


    
outp(P,219);



    
// numérisation


    
a1=(inp(P2))>>4;


    
outp(P,91);



    
a2=(inp(P2))>>4;


    
outp(P,155);


    
a3=(inp(P2))>>4;


    
outp(P,27);


    
a4=(inp(P2))>>4;



    
x=(a1+(a2<<4)+(a3<<8)+(a4<<12))^0x8888;


    
if (x>32767) x=32767;


    
*(PTR--)=x;


    
}


  p0=p0+jmax+1;


  /**** on retire
10 pixels à la fin ****/


  for (j=0;j<10;j++)

  
{


  
read_pel_fast(P);


  
}



  }

}


La routine READ_AUDINE2 réalise une numérisation de l'image
en binning 2x2 (le format final sera alors de 383x256 points). Cette routine
est très semblable de la précédente. Attachons nous
aux différences.


Bien sûr le format de l'image n'est plus le même :


imax=256;

jmax=384;


Ensuite nous transmettons 2 lignes dans le registre horizontal et non
plus une seule. Ces 2 lignes s'additionnent dans le registre,  ce
qui produit l'accumulation suivant l'axe vertical.


fast_line(P);


zi_zh(P);

zi_zh(P);

 

Enfin, nous lisons par paires les pixels, mais en prenant bien garde
de n'envoyer le top de reset que tous les 2 pixels seulement. L'absence
du top de reset, un pixel sur deux, produit l'effet désiré
d'accumulation suivant l'axe horizontal, la capacité de sortie n'étant
remise à zéro qu'un point image sur deux.


outp(P,247); 
// reset


outp(P,255); 
// palier de référence


outp(P,255);

outp(P,255);


outp(P,255);

outp(P,255);

outp(P,239); 
// clamp


outp(P,255);

outp(P,251); 
// palier vidéo


outp(P,255); 
// palier de référence



outp(P,251); 
// palier vidéo


outp(P,251);

outp(P,251);

outp(P,251);

outp(P,251);

outp(P,219); 
// start convert


outp(P,219);


outp(P,219);

outp(P,219);

outp(P,219);

outp(P,219);


Ce principe de cumul des charges, soit dans le registre horizontal,
soit dans la capacité de sortie est facile à étendre
pour obtenir n'importe quel facteur de binning.


/****************
read_audine2 *****************/


/* Audine                                    
*/


/*=============================================*/


/* Lecture en binning
2x2                    
*/


/***********************************************/

void read_audine2(short
base,short *buf)


{

short P,P2;

int x;

int a1,a2,a3,a4;

int i,j;


int imax,jmax;

short *PTR


imax=256;

jmax=384;


P=base;

P2=P+1;

PTR=buf;


/**** on retire les
4 premières lignes ****/


for (i=0;i<4;i++)

   {

   zi_zh(P);

   fast_line(P);

   }


for (i=0;i<imax;i++)

   {

   fast_line(P);

   zi_zh(P);

   zi_zh(P);

 


   /****
on retire les 14 premiers pixels ****/


   for
(j=0;j<14;j++)


    
{


    
read_pel_fast(P);


    
}



   p0=p0+imax-1;

   for
(j=0;j<jmax;j++)


    
{


    
outp(P,247);   // reset


    
outp(P,255);   // palier de référence



    
outp(P,255);


    
outp(P,255);


    
outp(P,255);


    
outp(P,255);


    
outp(P,239);   // clamp



    
outp(P,255);


    
outp(P,251);   // palier vidéo


    
outp(P,255);


    
outp(P,251);   // palier vidéo


    

outp(P,251);


    
outp(P,251);


    
outp(P,251);


    
outp(P,251);


    
outp(P,219);   // start convert


    

outp(P,219);


    
outp(P,219);


    
outp(P,219);


    
outp(P,219);


    
outp(P,219);



    
// numérisation



    
a1=(inp(P2))>>4;


    
outp(P,91);


    
a2=(inp(P2))>>4;


    
outp(P,155);


    
a3=(inp(P2))>>4;



    
outp(P,27);


    
a4=(inp(P2))>>4;


    
x=(a1+(a2<<4)+(a3<<8)+(a4<<12))^0x8888;


    
if (x>32767) x=32767;



    
*(PTR--)=x;


    
}


  p0=p0+jmax+1;


  /**** on retire
10 pixels à la fin ****/


  for (j=0;j<10;j++)


  
{


  
read_pel_fast(P);


  
}


  }

}



On trouvera ci-après le listing des fonctions élémentaires
appelées par READ_AUDINE et READ_AUDINE2.


/********************
zi_zh ********************/



/* Audine                                    
*/


/*=============================================*/

/* Transfert zone
image -> registre horizontal */


/***********************************************/

void zi_zh(short
base)


{


_asm

    
{


    
mov dx,base


    
mov al,11111011b


    
out dx,al


    
out dx,al



    
out dx,al


    
out dx,al


    
out dx,al


    
out dx,al


    
out dx,al


    
out dx,al



    
mov al,11111010b


    
out dx,al


    
out dx,al


    
out dx,al


    
out dx,al


    
out dx,al



    
out dx,al


    
out dx,al


    
out dx,al


    
mov al,11111001b


    
out dx,al


    
out dx,al



    
out dx,al


    
out dx,al


    
out dx,al


    
out dx,al


    
out dx,al


    
out dx,al



    
mov al,11111010b


    
out dx,al


    
out dx,al


    
out dx,al


    
out dx,al


    
out dx,al



    
out dx,al


    
out dx,al


    
out dx,al


    
mov al,11111011b


    
out dx,al


    
out dx,al



    
out dx,al


    
out dx,al


    
out dx,al


    
out dx,al


    
out dx,al


    
out dx,al



    
}


}


/****************
read_pel_fast ****************/


/* Audine                                    
*/


/*=============================================*/

/* Lecture rapide
d'un pixel                 
*/



/***********************************************/

void read_pel_fast_inv(short
base)


{


_asm

    
{


    
mov dx,base


    
mov al,11110111b



    
out dx,al


    
mov al,11111111b


    
out dx,al


    
mov al,11111011b


    
out dx,al


    
}



}


/********* fast_line
**********/


/* Audine                   
*/


/*============================*/

/* Lecture rapide
d'une ligne */


/******************************/

void fast_line(short
base)



{

short j;


for (j=0;j<794;j++)

   {

   read_pel_fast(base);

   }


}


La routine suivante, FAST_VIDAGE est utilisée pour lire rapidement
l'ensemble de la matrice CCD afin de supprimer toute charge électrique
juste avant de commencer une phase d'intégration. Lorsque vous voyez
apparaître le statut RAZ dans PISCO, juste avant que le décompte
du temps d'intégration ne débute, c'est cette routine qui
est appelée 3 ou 4 fois de suite. Notez que pour augmenter la vitesse,
on procède à un binning d'un facteur 4 dans le registre horizontal,
ce qui n'oblige à lire que 130 lignes environ.


/*********** fast_vidage
************/


/* Audine                         
*/


/*==================================*/

/* Lecture rapide
de la matrice     */


/************************************/


void fast_vidage(short
base)


{

short i,j;


/*---- LECTURE DU
REGISTRE HORIZONTAL ----*/


for (i=0;i<130;i++)

   {

   zi_zh(base);


   zi_zh(base);

   zi_zh(base);

   zi_zh(base);

   for
(j=0;j<794;j++)


    
{


    

read_pel_fast(base);


    
}


   }

}


La routine suivante, READ_AUDINE_DOUBLE_ADC,
illustre la souplesse de fonctionnement de la caméra Audine. La
différence entre le palier vidéo et le palier de référence
n'est plus effectuée par une méthode analogique (le circuit
de clamp), mais par une méthode purement logicielle. La routine
réalise deux numérisations, une sur le palier de référence,
une autre sur le palier vidéo. Le signal vidéo proprement
dit est la différence de ces deux conversions. Attention au AD976,
la valeur numérique qu'il délivre est en fait le résultat
de la conversion précédant celle que vous venez de réaliser.
Il faut en tenir compte (avec ce listing, qui présente une routine
expérimentale, le premier pixel de chaque ligne image n'a pas une
valeur valide à cause de cela). Notez que le circuit de clamp n'est
jamais activé lors de la lecture de l'image.


/********** read_audine_double_ADC
*************/


/* Audine                                    
*/



/*=============================================*/

/* Lecture en binning
1x1                    
*/


/* et double échantillonnage
numérique         */


/***********************************************/

void read_audine_double_ADC(short
base,short *buf)


{

short P,P2;


int x,x1,x2;

int a1,a2,a3,a4;

int i,j;

int imax,jmax;

short *PTR


imax=512;

jmax=768;


P=base;


P2=P+1;

PTR=buf;


/**** on retire les
4 premières lignes ****/


for (i=0;i<4;i++)

   {

   zi_zh(P);

   fast_line(P);


   }


for (i=0;i<imax;i++)

   {

   fast_line(P);

   zi_zh(P);

 


   /****
on retire les 14 premiers pixels ****/


   for
(j=0;j<14;j++)


    
{


    
read_pel_fast(P);


    
}



   p0=p0+imax-1;

   x2=0;

   for
(j=0;j<jmax;j++)


    
{


    
outp(P,247);   // reset



    
outp(P,255);   // palier de référence


    
outp(P,255);


    
outp(P,255);


    
outp(P,255);


    
outp(P,255);



    
outp(P,223);   // start convert sur la palier de référence


    
outp(P,223);


    
outp(P,223);


    
outp(P,223);


    
outp(P,223);



    
outp(P,223);



    
// numérisation


    
a1=(inp(P2))>>4;


    
outp(P,95);


    
a2=(inp(P2))>>4;



    
outp(P,159);


    
a3=(inp(P2))>>4;


    
outp(P,31);


    
a4=(inp(P2))>>4;


    
x1=(a1+(a2<<4)+(a3<<8)+(a4<<12))^0x8888;



    
if (x1>32767) x1=32767;


    
x=x1-x2;       // double échantillonnage
numérique



    
outp(P,255);


    
outp(P,251);   // palier vidéo



    
outp(P,251);


    
outp(P,251);


    
outp(P,251);


    
outp(P,219);   // start convert sur le palier vidéo


    
outp(P,219);



    
outp(P,219);


    
outp(P,219);


    
outp(P,219);


    
outp(P,219);



    
// numérisation


    

a1=(inp(P2))>>4;


    
outp(P,91);


    
a2=(inp(P2))>>4;


    
outp(P,155);


    
a3=(inp(P2))>>4;



    
outp(P,27);


    
a4=(inp(P2))>>4;


    
x2=(a1+(a2<<4)+(a3<<8)+(a4<<12))^0x8888;


    
if (x2>32767) x2=32767;



    
*(PTR--)=x;


    
}


  p0=p0+jmax+1;


  /**** on retire
10 pixels à la fin ****/


  for (j=0;j<10;j++)


  
{


  
read_pel_fast(P);


  
}


  }

}




L'extrait de routine suivant montre comment il faut
s'y prendre pour mettre en oeuvre le mode drift-scan.
Il faut fournir la dimension du scan (NB_LIGNE) ainsi que le délai
en secondes entre deux lectures de lignes consécutives (DELAI).
La fonction CLOCK() retourne le temps courant absolu en centièmes
de secondes.



imax=nb_ligne;

jmax=768;


/**** on supprime
les 4 premières lignes ****/


for (i=0;i<4;i++)

   {

   zi_zh_13(P);

   fast_line(P);


   }


n=1.0;

first=clock();


i=0;

while(1)

   {

   courant=clock()-first;


   if ((double)courant/100.0>=n*delai)

    
{


    
fast_line(P);


    
zi_zh_13(P);



    
/**** on retire les 14 premiers pixels ****/



    
for (j=0;j<14;j++)


       
{


       
read_pel_fast(P);


       
}



    
p0=p0+jmax-1;



    
for (j=0;j<jmax;j++)


       
{


       
outp(P,247);   // reset


       
outp(P,255);   // palier de référence



       
outp(P,255);


       
outp(P,255);


       
outp(P,255);


       
outp(P,255);


       
outp(P,239);   // clamp



       
outp(P,255);


       
outp(P,251);   // palier vidéo


       
outp(P,251);


       
outp(P,251);


       
outp(P,251);



       
outp(P,219);   // start convert


       
outp(P,219);


       
outp(P,219);


       
outp(P,219);


       
outp(P,219);



       
outp(P,219);



       
// numérisation


       
a1=(inp(P2))>>4;


       
outp(P,91);


       
a2=(inp(P2))>>4;



       
outp(P,155);


       
a3=(inp(P2))>>4;


       
outp(P,27);


       
a4=(inp(P2))>>4;


       
x=(a1+(a2<<4)+(a3<<8)+(a4<<12))^0x8888;



       
if (x>32767) x=32767;


       
*(PTR--)=x;


       
}


    
p0=p0+jmax+1;



    
/**** on retire 10 pixels à la fin ****/



    
for (j=0;j<10;j++)


       
{


       
read_pel_fast(P);


       
}


    
i++;



   
/* Gérer ici l'arret de l'acquisition en continu par l'appui d'une
touche


      
imax=i-1;


      
...


      
...


   
*/


   
if (i==imax)



      
{


      
/* Arrèter ici l'acquisition en continu


         
...


         
...


      
*/


      
}



   
/* on affiche ici l'écart en seconde avec le temps nominal devant
s'écouler entre


      
la lecture de 2 lignes. Notez qu'un écart éventuel est compensé
lors de la


      
lecture de la ligne suivante.


   
*/


    
sprintf(text,"ligne:%d   (delta T:%.3f)",i,(double)courant/100.0-n*delai);



    
printf("%s\n",text);


    
n=n+1.0;


    
}


   }


L'extrait de routine qui suit montre comment il
faut s'y prendre pour mettre en oeuvre le mode vidéo. La routine lit à grande vitesse une zone verticale
de l'image, centrée sur la colonne POSX et de largeur TAILLE. L'acquisition
fait NB_LIGNE de hauteur.


imax=nb_ligne;


jmax=taille;


/**** on retire les
4 premières lignes ****/


for (i=0;i<4;i++)

   {

   zi_zh(P);

   fast_line(P);


   }


posx=768-posx;

cx1=posx-taille/2+14;

cx2=768-(cx1+taille-10);


i=0;

while(1)

   {


   while(1)

    
{


    
for (k=0;k<taille;k++) zi_zh(P);


    
fast_line(P);



    
/* gérer ici le temps d'intégration (bref en mode vidéo)



       
.....


       
.....


    
*/



    
for (k=0;k<taille;k++) zi_zh(P);


    
fast_line(P);



    
for (k=0;k<taille;k++)


       
{


       
zi_zh(P);


 

       
/**** on retire les cx1 premiers pixels ****/


       
for (j=0;j<cx1;j++)



          
{


          
read_pel_fast(P);


          
}



       
p0=p0+imax-1;


       
for (j=0;j<jmax;j++)



          
{


          
outp(P,247);   // reset


          
outp(P,255);   // palier de référence


          
outp(P,255);


          

outp(P,255);


          
outp(P,255);


          
outp(P,255);


          
outp(P,239);   // clamp


          
outp(P,255);


          

outp(P,251);   // palier vidéo


          
outp(P,251);


          
outp(P,251);


          
outp(P,251);


          
outp(P,219);   // start convert



          
outp(P,219);


          
outp(P,219);


          
outp(P,219);


          
outp(P,219);


          
outp(P,219);



          

// numérisation


          
a1=(inp(P2))>>4;


          
outp(P,91);


          
a2=(inp(P2))>>4;


          
outp(P,155);


          

a3=(inp(P2))>>4;


          
outp(P,27);


          
a4=(inp(P2))>>4;


          
x=(a1+(a2<<4)+(a3<<8)+(a4<<12))^0x8888;


          

if (x>32767) x=32767;


          
*(PTR--)=x;


       
}


    
p0=p0+jmax+1;


    
/**** on retire cx2 pixels à la fin ****/


    
for (j=0;j<cx2;j++)



       
{


       
read_pel_fast(P);


       
}


       
i++;


   
/* Gérer ici l'arret de l'acquisition en continu par l'appui d'une
touche


      
imax=i-1;



      
...


      
...


   
*/


   
if (i==imax)


      
{


      
/* Arrèter ici l'acquisition en continu



         
...


         
...


      
*/


      
}


   
}


  }

Aucun commentaire:

Enregistrer un commentaire