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 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