#include #include #include #include #include #include "phlib.h" #include "phdriver.h" /* This file 'phlib.c' contains a collection of functions that makes 'ioctl' calls to the device driver to perform hardware related functions. The structure and the array of structures defined below is used for sending and receiving data from the driver. The structure 'timeval', that has second and microsecond valiables for storing the time information, is used in almost all the calls. IMPORTANT: Programs calling 'phlib' MUST include the file 'phlib.h' */ int fd; drvdata ddata[MAXPOINTS]; // defined in phdriver.h boolean open_phoenix (void) { /* For this call to succeed the driver should be loaded earlier using 'insmod' command and the device file '/dev/phoenix' should exist. The script 'loading.sh' creates this file. */ fd = open ("/dev/phoenix", O_RDWR); if (fd < 0) { fprintf (stderr, "Phoenix driver open failed\n"); exit (1); } return TRUE; } int read_inputs (void) { /* Returns a value from 0 to 15 depends on the voltage levels present at the four digital input pins of the phoenix box. Returns 255 on error */ int dat; if (ioctl (fd, DIGIN, &dat) < 0) { fprintf (stderr, "DIGIN failed\n"); return 255; // error return } return dat & 15; } boolean write_outputs (int dat) { /* Writes a value from 0 to 255 to the eight digital outputs of phoenix */ if (ioctl (fd, DIGOUT, &dat) < 0) { fprintf (stderr, "DIGOUT failed\n"); return FALSE; } return TRUE; } boolean set_dac (int dat) /* input 0 to 255 */ { /* Sets the output of the programmable voltage source. It is an 8 bit DAC that takes 0 to 255 input and sets output from -5V to +5V. */ if (ioctl (fd, SETDAC, &dat) < 0) { fprintf (stderr, "SETDAC failed\n"); return FALSE; } return TRUE; } boolean set_voltage (double val) /* input from -5000 to 5000 mV */ { /* Sets the output of the programmable voltage source. It is an 8 bit DAC that takes 0 to 255 input and sets output from -5V to +5V. This function is called with argument from -5000 to + 5000 millivolts. */ static const double m = 10000.0 / 255; int dat; if ((val < -5000.0) || (val > 5000.0)) return FALSE; dat = (int) (128.0 + val / m); if (ioctl (fd, SETDAC, &dat) < 0) { fprintf (stderr, "SETDAC failed\n"); return FALSE; } return TRUE; } boolean write_motor (int dat) { if (ioctl (fd, SETMOTOR, &dat) < 0) { fprintf (stderr, "SETMOTOR failed\n"); return FALSE; } return TRUE; } boolean rotate_motor (int nsteps, int dir) { if (dir) { if (ioctl (fd, MOTORCW, &nsteps) < 0) { fprintf (stderr, "MOTORCW failed\n"); return FALSE; } } else { if (ioctl (fd, MOTORCCW, &nsteps) < 0) { fprintf (stderr, "MOTORCCW failed\n"); return FALSE; } } return TRUE; } boolean select_adc (int chan) { /* Four analog inputs connected the ADC through Sample and Hold circuits. Any value between 0 to 3 selects corresponding channel during the next digitization. Adding 4 to the channel number will prevent the Sampling during the next digitization and only converts the existing voltage on the HOLD capacitor. This is useful for simultanious digitization of multiple channels. This feature is used internally by the driver and library functions are provided for multiple channel digitizations. */ if (chan > 7) return FALSE; if (ioctl (fd, SELECTADC, &chan) < 0) { fprintf (stderr, "SELECTADC failed\n"); return FALSE; } return TRUE; } int read_adc (double *ts) /* returns 0 to 255 */ { if (ioctl (fd, READADC, ddata) < 0) { fprintf (stderr, "READADC failed.\n"); return -1; } *ts = (double) ddata[0].t.tv_sec + (double) ddata[0].t.tv_usec / 1.0e6; return ddata[0].adval[0] & 0xff; } double zero_to_5000 (void) /* returns 0 to 5000 mV */ { static const double m = 5000.0 / 255; double ts; return m * read_adc(&ts); } double minus5000_to_5000 (void) /* returns -5000 to 5000 mV */ { /* The ADC takes only 0 to 5V. To digitize bipolar signals, -5V to 5V range, they are connected to the ADC input through the (x+5)/2 amplifier. In that case use this function that will return a a value from -5000 to 5000 millivolts. */ static const double m = 10000.0 / 255; double ts; return m * read_adc(&ts) - 5000.0; } double minus5000_to_5000TS (double *ts) /* returns -5000 to 5000 mV */ { /* The ADC takes only 0 to 5V. To digitize bipolar signals, -5V to 5V range, they are connected to the ADC input through the (x+5)/2 amplifier. In that case use this function that will return a a value from -5000 to 5000 millivolts. ts will return the timestamp. */ static const double m = 10000.0 / 255; return m * read_adc(ts) - 5000.0; } boolean read_block (int npoints, int delay, boolean bipolar, double *tstamp, double *adval) { /* Does a specified number of digitizations from the channel selected before npoints : number of digitizations.(upto MAXPOINTS=1024) delay : time interval between two digitizations. minimum is around 115 usec bipolar : If TRUE adval elements will be from -5000 to 5000. Else 0 to 5000 mV tstamp : array of type double to return the time stamps adval : arrayof type double to return adc output */ int k, *ip; double strt; if (npoints > MAXPOINTS) { fprintf(stderr, "Number of samples limited to %d\n", MAXPOINTS); return FALSE; } ip = (int *) ddata; *ip++ = npoints; *ip = delay; if (ioctl (fd, READBLOCK, ddata) < 0) { fprintf (stderr, "READBLOCK failed\n"); return FALSE; } strt = (double) ddata[0].t.tv_sec + (double) ddata[0].t.tv_usec / 1.0e6; for (k = 0; k < npoints; ++k) { *tstamp++ = (double) ddata[k].t.tv_sec + (double) ddata[k].t.tv_usec / 1.0e6 - strt; if (bipolar) { double m = 10000.0 / 255; *adval++ = m * (ddata[k].adval[0] & 0xff) - 5000; } else { double m = 5000.0 / 255; *adval++ = m * (ddata[k].adval[0] & 0xff); } } return TRUE; } boolean multi_read_block (int npoints, int nchan, int delay, double *t, int *a, int *b, int *c, int *d) { /* Does a specified number of digitizations. npoints : number of digitizations.(upto MAXPOINTS=1024) nchan : number of channels to read (1 to 4). Starts with zero only delay : time interval between two digitizations. minimum is around 115 usec t : double array for timestamp a,b,c,d : int arrays for data (fills only the first nchan ones) */ int k, *ip; double strt; if (npoints > MAXPOINTS) return FALSE; ip = (int *) ddata; *ip++ = npoints; *ip++ = nchan; *ip = delay; if (ioctl (fd, MULTIREADBLOCK, ddata) < 0) { fprintf (stderr, "MULTIREADBLOCK failed\n"); return FALSE; } strt = (double) ddata[0].t.tv_sec + (double) ddata[0].t.tv_usec / 1.0e6; for (k = 0; k < npoints; ++k) { t[k] = (double) ddata[k].t.tv_sec + (double) ddata[k].t.tv_usec / 1.0e6 - strt; a[k] = ddata[k].adval[0]; if(nchan == 1) continue; b[k] = ddata[k].adval[1]; if(nchan == 2) continue; c[k] = ddata[k].adval[2]; if(nchan == 3) continue; d[k] = ddata[k].adval[3]; } return TRUE; } boolean trig_read_block (int pin, int npoints, int deadtime, int delay, boolean trig_pol, double *tstamp, int *adval) { /* Similar to above but digitization is triggered by a falling edge. pin : Digital input pin to wait for the falling edge (0 to 3) npoints : number of digitizations.(upto MAXPOINTS=1024) deadtime: time interval from the Falling Edge to the first digitization. delay : Time interval between samples bipolar : If TRUE adval elements will be from -5000 to 5000. Else 0 to 5000 mV tstamp : array of type double to return the time stamps adval : arrayof type double to return adc output */ int k, *ip; double strt; if (npoints > MAXPOINTS) return FALSE; ip = (int *) ddata; *ip++ = pin; *ip++ = npoints; *ip++ = deadtime; *ip++ = delay; *ip = trig_pol; if (ioctl (fd, TRIGREADBLOCK, ddata) < 0) { fprintf (stderr, "TRIGREADBLOCK failed\n"); return FALSE; } strt = (double) ddata[0].t.tv_sec + (double) ddata[0].t.tv_usec / 1.0e6; for (k = 0; k < npoints; ++k) { *tstamp++ = (double) ddata[k].t.tv_sec + (double) ddata[k].t.tv_usec / 1.0e6 - strt; *adval++ = ddata[k].adval[0] & 0xff; } return TRUE; } //------------------Time difference measurement functions-------------------- double timer(int cmd, int pin1, int pin2) // internal helper function { double t0, t1; unsigned int *ip = (unsigned int *) ddata; *ip++ = pin1; *ip = pin2; if (ioctl (fd, cmd, ddata) < 0) { fprintf(stderr, "Timer call %d failed on %d to %d\n",cmd, pin1, pin2); return -1.0; } t0 = (double) ddata[0].t.tv_sec * 1.0e6 + (double) ddata[0].t.tv_usec; t1 = (double) ddata[1].t.tv_sec * 1.0e6 + (double) ddata[1].t.tv_usec; return t1 - t0; } double period (int bit) // returns negative value on error { /* Measures the time interval for 10 cycles of the input waveform to calculate the Period. */ unsigned int *ip = (unsigned int *) ddata; double t0, t1; *ip++ = bit; *ip = 10; // measure for 10 cycles if (ioctl (fd, PERIOD, ddata) < 0) // returns 2 time stamps { fprintf(stderr, "PERIOD failed\n"); return -1.0; } t0 = (double) ddata[0].t.tv_sec * 1.0e6 + (double) ddata[0].t.tv_usec; t1 = (double) ddata[1].t.tv_sec * 1.0e6 + (double) ddata[1].t.tv_usec; //printf("%f %f %f\n", t0,t1, t1-t0); return (t1 - t0) / 10.0; } double r2rtime (int pin1, int pin2) { /* Returns the time interval between a rising edge on pin1 to a rising edge on pin2 in microseconds. If same pin is specified it will return the interval between two consecutive rising edges. Returns -1 on error. */ return timer(R2RTIME, pin1,pin2); } double r2ftime (int pin1, int pin2) { /* measures the time interval between a rising edge on pin1 to a falling edge on pin2. If same pin is specified it will return the 'high time' of the TTL pulse fed to that pin. Also used for testing the accuracy of the system. */ return timer(R2FTIME, pin1,pin2); } double f2rtime (int pin1, int pin2) { /* measures the time interval between a falling edge on pin1 to a rising edge on pin2. If same pin is specified it will return the 'low time' of the TTL pulse fed to that pin. */ return timer(F2RTIME, pin1,pin2); } double f2ftime (int pin1, int pin2) { /* Returns the time interval between a falling edge on pin1 to a falling edge on pin2 in microseconds. If same pin is specified it will return the time between two consecutive falling edges */ return timer(F2FTIME, pin1,pin2); } double set2rtime (int pin1, int pin2) { /* Returns the time interval from setting an output bit to the rising edge on an input pin. pin1 value: 0 to 7 means the Digital output pins. 8 to 11 => stepper motor outputs A,B,C and D pin2 : Input pins 0 to 4 */ return timer(SET2RTIME, pin1,pin2); } double set2ftime (int pin1, int pin2) { /* Returns the time interval from setting an output bit to the rising edge on an input pin. pin1 value: 0 to 7 means the Digital output pins. 8 to 11 => stepper motor outputs A,B,C and D pin2 : Input pins 0 to 4 */ return timer(SET2FTIME, pin1,pin2); } double clr2rtime (int pin1, int pin2) { /* Returns the time interval from clearing an output bit to the rising edge on an input pin. pin1 value: 0 to 7 means the Digital output pins. 8 to 11 => stepper motor outputs A,B,C and D pin2 : Input pins 0 to 4 */ return timer(CLR2RTIME, pin1,pin2); } double clr2ftime (int pin1, int pin2) { /* Returns the time interval from clearing an output bit to the falling edge on an input pin. pin1 value: 0 to 7 means the Digital output pins. 8 to 11 => stepper motor outputs A,B,C and D pin2 : Input pins 0 to 4 */ return timer(CLR2FTIME, pin1,pin2); } double pulse2rtime (int pin1, int pin2, int width, int deadtime, int pol) { /* Generates a single pulse on pin1 and wait for a rising edge on pin2. pin1: 0 to 7 for digital outputs, 8 to 11 for stepper motor outputs pin2: 0 to 3 width: width of the pulse, Assumes the output was zero when called deadtime: Starts waiting only after elapsing deadtime. */ double t0, t1; unsigned int *ip = (unsigned int *) ddata; *ip++ = pin1; *ip++ = pin2; *ip++ = width; *ip++ = deadtime; *ip = pol; if (ioctl (fd, PULSE2RTIME, ddata) < 0) { fprintf (stderr, "ECHOTIME failed %f\n", -1.0); return -1.0; } t0 = (double) ddata[0].t.tv_sec * 1.0e6 + (double) ddata[0].t.tv_usec; t1 = (double) ddata[1].t.tv_sec * 1.0e6 + (double) ddata[1].t.tv_usec; // *mid = t0 + (t1-t0)/2.0; return t1 - t0; } double pulse2ftime (int pin1, int pin2, int width, int deadtime, int pol) { /* Generates a single pulse on pin1 and wait for a Falling edge on pin2. pin1: 0 to 7 for digital outputs, 8 to 11 for stepper motor outputs pin2: 0 to 3 width: width of the pulse, Assumes the output was zero when called deadtime: Starts waiting only after elapsing deadtime. */ double t0, t1; unsigned int *ip = (unsigned int *) ddata; *ip++ = pin1; *ip++ = pin2; *ip++ = width; *ip++ = deadtime; *ip = pol; if (ioctl (fd, PULSE2FTIME, ddata) < 0) { fprintf (stderr, "ECHOTIME failed %f\n", -1.0); return -1.0; } t0 = (double) ddata[0].t.tv_sec * 1.0e6 + (double) ddata[0].t.tv_usec; t1 = (double) ddata[1].t.tv_sec * 1.0e6 + (double) ddata[1].t.tv_usec; // *mid = t0 + (t1-t0)/2.0; return t1 - t0; } //----------------Absolute Time marking finctions------------------------ double mid_high_time (int pin) { /* Returns the time of occurance of a Positive TTL pulse on the specified digital input. value returned in the number of microseconds elapsed since the Epoh (see the man page of gettimeofday). This function is used for measuring time intervals in the order of seconds by subtracting the values from two calls. */ double t0, t1; unsigned int *ip = (unsigned int *) ddata; *ip++ = pin; *ip = pin; if (ioctl (fd, R2FTIME, ddata) < 0) { fprintf (stderr, "R2FTIME failed\n"); return -1.0; } t0 = (double) ddata[0].t.tv_sec * 1.0e6 + (double) ddata[0].t.tv_usec; t1 = (double) ddata[1].t.tv_sec * 1.0e6 + (double) ddata[1].t.tv_usec; return t0 + (t1 - t0) / 2.0; } double mid_low_time (int pin) { // Similar to above but looks for a negative TTL pulse. double t0, t1; unsigned int *ip = (unsigned int *) ddata; *ip++ = pin; *ip = pin; if (ioctl (fd, F2RTIME, ddata) < 0) { // fprintf(stderr, "F2RTIME failed\n"); return -1.0; } t0 = (double) ddata[0].t.tv_sec * 1.0e6 + (double) ddata[0].t.tv_usec; t1 = (double) ddata[1].t.tv_sec * 1.0e6 + (double) ddata[1].t.tv_usec; return t0 + (t1 - t0) / 2.0; } //---------------------------------------------------------------------- double pulse_out (int pin, int high, int low, int np, int pol) { /* generates a pulse on the TTL digital output or the stepper motor output. pin : 0 to 7 means the bits on digital output. 8 tp 11 on the Motor outputs. high : HIGH time of the pulse in usecs low : LOW time of the pulse in usecs np : number of pulses to be generated pol : polarity of the pulse. 0 for +ive TTL 1 for -ive TTL */ double t0, t1; unsigned int *ip = (unsigned int *) ddata; *ip++ = pin; *ip++ = high; *ip++ = low; *ip++ = np; *ip = pol; if (ioctl (fd, PULSEOUT, ddata) < 0) { fprintf (stderr, "PULSEOUT failed\n"); return -1.0; } t0 = (double) ddata[0].t.tv_sec * 1.0e6 + (double) ddata[0].t.tv_usec; t1 = (double) ddata[1].t.tv_sec * 1.0e6 + (double) ddata[1].t.tv_usec; return t0; } int set_timeout (unsigned int i) // value from 5000 to 5000000 { /* Time measurement calls wait inside the kernel and freezes the system for that period. It is essential to provide maximum limits for waiting. The default is around 2 seconds. This can be set between 5 milli second and 5 second. */ if (ioctl (fd, SETMAXWAIT, &i) < 0) { fprintf(stderr, "Set Timeout failed for %d usecs\n",i); return 0; } return i; } double timestamp(void) // returns the absolute time { double t0; if (ioctl (fd, TIMESTAMP, ddata) < 0) { fprintf(stderr, "Timestamp: error\n"); return -1.0; } t0 = (double) ddata[0].t.tv_sec * 1.0e6 + (double) ddata[0].t.tv_usec; printf("%g\n", t0); return t0; } boolean start_hist(void) { if(ioctl(fd, START_HIST, NULL) < 0) return FALSE; return TRUE; } boolean stop_hist(void) { if(ioctl(fd, STOP_HIST, NULL) < 0) return FALSE; return TRUE; } boolean clear_hist(void) { if(ioctl(fd, CLEAR_HIST, NULL) < 0) return FALSE; return TRUE; } boolean read_hist(int* hist) { if(ioctl(fd, READ_HIST, hist) < 0) return FALSE; return TRUE; } // main for tesing purpose only /* main() { int counter, k, hist[4096]; open_phoenix(); start_hist(); sleep(1); stop_hist(); read_hist(hist); counter = 0; for(k = 0; k < 4096; ++k) if(hist[k]) { counter += hist[k]; printf("%d %d\n", k, hist[k]); } printf("counter = %d\n", counter); } */