"""Bibliothèque Python pour accéder au pilote de noyau de Phoenix. Le pilote de noyau de Phoenix fournit un ensemble de fonctions pour permettre d'effectuer des mesures de temps précises, des conversions A/N et N/A, des E/S numériques, etc. Comme toutes les actions liées à la gestion du temps et du matériel sont effectuées dans le noyau, nous avons la liberté d'utiliser un langage comme Python qui n'est traditionnellement pas approprié pour le travail en temps réel. """ SETDAC = 0 SELECTADC = 1 READADC = 2 READBLOCK = 3 DIGIN = 4 DIGOUT = 5 MOTORCW = 6 MOTORCCW = 7 SETMOTOR = 8 R2RTIME = 9 R2FTIME = 10 F2RTIME = 11 F2FTIME = 12 PERIOD = 13 SETMAXWAIT = 14 PULSEOUT = 15 SLOWPULSEOUT = 16 DROPTIME = 17 TREADBLOCK = 18 MAXPOINTS = 1024 import fcntl, types import os, struct, array def sequence_to_int(a): r = 0 for i in range(8): r = (r << 1) | int(a[i]) return r class Phoenix: def __init__(self, dev='/dev/phoenix'): try: self.fd = os.open(dev, os.O_RDONLY) except: print 'Impossible d'ouvrir le périphérique phoenix : ', dev raise SystemExit # Placer le matériel dans un état 'connu' self.adc_chan = 0 # Canal ADC 0 fcntl.ioctl(self.fd, SELECTADC, 0) # Sortie DAC à 0V fcntl.ioctl(self.fd, SETDAC, 0) # La totalité des huit broches numériques de sortie définies à zéro fcntl.ioctl(self.fd, DIGOUT, 0) # Les quatre broches du pilotes de moteur à pas définis à zéro fcntl.ioctl(self.fd, SETMOTOR, 0) def read_inputs(self): """Renvoie un tableau contenant 4 nombres représentant les niveaux logiques sur les quatre broches numériques d'entrée. Usage: p = Phoenix() p.read_inputs() Supposons que le tableau renvoyé soit [1,0,0,1]. Cela signifie que la broche numérique d'entrée la plus à gauche sur le boîtier de Phoenix a une logique haute, la suivante une basse, et ainsi de suite...""" # Quand le troisième argument à ioctl est un tampon # immuable, il renvoie une chaîne de même # longueur contenant les données que l'ioctl du noyau # a écrites dans le tampon 'C' qu'il a obtenu comme # argument. r = ord(fcntl.ioctl(self.fd, DIGIN, ' ')) a = [] for i in range(4): a.append(r & 1) r = r >> 1 return a def write_outputs(self, val): """Écrit une valeur sur 8 bits sur les broches numériques de sortie. Usage: p = Phoenix() p.write_outputs(0x81) p.write_outputs((1,0,0,0,0,0,0,1)) p.write_outputs('10000001') """ if ((type(val) == types.ListType) or (type(val) == types.TupleType) or (type(val) == types.StringType)): val = sequence_to_int(val) r = fcntl.ioctl(self.fd, DIGOUT, val) elif type(val) == types.IntType: r = fcntl.ioctl(self.fd, DIGOUT, val) else: print 'Invalid type to write_outputs' return assert r == 0 def set_voltage(self, val): """Définit la tension de sortie du convertisseur numérique->analogique. Usage: p = Phoenix() p = set_dac_mv(-2345) # o/p -2345 mV p = set_dac_mv(4315) # o/p 4315 mV Note : l'argument 'val' devra être compris entre -5000.0 et +5000.0.""" if ((val < -5000) or (val > 5000)): print 'la broche de sortie DAC devra être comprise entre -5000 et +5000 mV' return m = 10000.0/255.0 dat = int(128.0 + (val/m)) r = fcntl.ioctl(self.fd, SETDAC, dat) assert r == 0 def select_adc(self, chan): """Sélectionne le canal CAN à utiliser. Usage: p = Phoenix() p.select_adc(0) Note : le numéro de canal devra être compris entre 0 et 7 """ if chan > 7: print 'le numéro de canal CAN devra être 0 - 7' return self.adc_chan = chan r = fcntl.ioctl(self.fd, SELECTADC, chan) assert r == 0 def read_adc(self): """Lit depuis le canal ADC actuel et renvoie un nombre compris entre 0 et 255. Usage: p = Phoenix() p.read_adc() """ # similaire à read_inputs s = fcntl.ioctl(self.fd, READADC, ' ') return ord(s) def zero_to_5000(self): """Lit depuis le CAN et renvoie une tension comprise dans la plage 0 à +5000 mV. Usage: p = Phoenix() p.zero_to_5000() """ return (5000.0/255) * self.read_adc() def minus5000_to_5000(self): """Lit depuis le CAN et renvoie une tension comprise dans la plage -5000 à +5000 mV. Le CAN accepte que des entrées de 0 à 5V. Un signal bipolaire sera fourni à l'entrée CAN après l'avoir augmentée dans la plage 0-5V. Cette fonction interprète simplement la sortie numérique de le CAN comme une tension comprise dans la plage -5000 à +5000 mV.""" return ((10000.0/255) * self.read_adc()) - 5000.0 def read_block(self, npoints=1024, delay=0, bipolar=0): """Lire un bloc de données depuis le canal CAN actuel et renvoie un tableau contenant des estampilles temporelles ainsi que des valeurs échantillonnées. Usage: p = Phoenix() p.read_block(10, 0) La liste renvoyée est de la forme : [(timestamp1, adval1), (timestamp2, adval2) ....] Si bipolar=1, la valeur renvoyée sera comprise entre -5000mV et +5000mV. L'argument 'delay' spécifie un délai en micro-secondes entre deux lectures du CAN. """ if (npoints > MAXPOINTS): print 'Seuls les points ', MAXPOINTS, 'peuvent être échantillonnés ', print 'à la fois.' return buf = array.array('I',[0,0,0] * npoints) buf[0] = npoints buf[1] = delay r = fcntl.ioctl(self.fd, READBLOCK, buf, 1) assert r == 0 dat = [] i = 0; j = 0 start = buf[0] + buf[1]/1.0e6 while (i < npoints): t = (buf[j] + buf[j+1]/1.0e6) - start if(bipolar): adval = buf[j+2] * (5000.0/255.0) else: adval = buf[j+2] * (10000.0/255.0) - 5000.0 dat.append((t, adval)) i = i + 1 j = j + 3 return dat def trig_read_block(self, npoints=1024, delay=0, bipolar=0): """Lit un bloc de données depuis le canal CAN actuel et renvoie un tableau contenant des estampilles temporelles ainsi que des valeurs échantillonnées. La lecture ne démarre que quand la broche numérique d'entrée arrive à zéro. Usage: p = Phoenix() p.trig_read_block(10, 0) La liste renvoyée est de la forme : [(timestamp1, adval1), (timestamp2, adval2) ....] Si bipolar=1, la valeur renvoyée sera comprise entre -5000mV et +5000mV. L'argument 'delay' spécifie un délai en micro-secondes entre deux lectures du CAN. """ print 'bipolar: ', bipolar if (npoints > MAXPOINTS): print 'Seuls les points ', MAXPOINTS, 'peuvent être échantillonnés ', print 'à la fois.' return buf = array.array('I',[0,0,0] * npoints) buf[0] = npoints buf[1] = delay r = fcntl.ioctl(self.fd, TREADBLOCK, buf, 1) assert r == 0 dat = [] i = 1; j = 3 # Ignorer le premier point start = buf[0] + buf[1]/1.0e6 while (i < npoints): t = (buf[j] + buf[j+1]/1.0e6) - start if(bipolar): adval = buf[j+2] * (10000.0/255.0) - 5000.0 else: adval = buf[j+2] * (5000.0/255.0) dat.append((t, adval)) i = i + 1 j = j + 3 return dat def write_motor(self, dat): """Écrit un motif sur 4 bits sur les broches du pilote de moteur pas à pas. Utile lors de l'utilisation de bobines de relais.""" r = fcntl.ioctl(self.fd, SETMOTOR, dat) assert r == 0 def get_drop_time(self): """Mesure le temps de vol en faisant tomber une boule retenue par une bobine de relais. Renvoie le temps en micro-secondes. """ buf = array.array('I', [0,0,0]*2) r = fcntl.ioctl(self.fd, DROPTIME, buf, 1) assert r == 0 t0 = buf[0] * 1.0e6 + buf[1] t1 = buf[3] * 1.0e6 + buf[4] return t1 - t0 --Boundary-