/* parda2.c: A simple linux user space program for the simple DA-converter as a function generator via the Parallel Port and 8 330 Ohm resitors between every data pin and the output (= 3 1/2 -bit DAC, short-circuit-proof). This is the continuation of parda.c but with a second DA-converter at control pins (4x 330 Ohm). Compile with optimisation (-O2). Parameter: I/O-Address (usually 888 for standard parallel port), Byte of the data register (pin pattern on output), duration of a high/low period at the data pins in µs, Byte of the control register (pin pattern on output), duration of a high/low period at the control pins in µs. Time in µs between two samples. Example (for root): ./parda2 888 255 20000 15 169000 0 (106 RPM, 175 PPM) The bit patter of the output determins the amplitude: 8 bits (255) means 100 %; 0 means 0 %, 4 bits (e. g. 15) means 50 % etc. (at the 8 data pins). Measured frequency with Linux version 2.6.8-24.11-smp (SuSE 9.2) and the onboard port: delay frequency/Hz (onboard port) 10000000 0.05 1000000 0.4988 248000 2.00 100000 4.9 10000 41,6 1000 166 1 ... 100 249,9 0 499,9 0 without udelay approx. 500 kHz The same was measured with a parallel port PCI card. For higher frequencies you should use a kernel module which should have a limit of approx. 100 kHz instead of 500 kHz. Todo: Check of the parameters + help, a function for triangle wave output with the output sequence 0 1 3 7 15 31 63 127 255 254 252 248 240 224 192 128 0 ..., a function for sine wave output, ..., get address from /proc/bus/pci from first device with vendor id 0x9710 and device id 0x9805. Change usleep -> select (more portable). Two edge triggered frequency measurement. RTAI Version. Rolf Freitag May 2005, rolf.freitag at web.de */ #include #include /* or asm/io.h; for inb, ioperm, iopl ... */ #include /* atoi */ #include // getuid(), usleep #include // EPERM #include #include // signals #include #include #include static int value2; // bit pattern for the control pins (0 = off, 15 = all high) static int delay2; // duration of a pulse/leap in µs for the control output static int delay3; // duration of a sample static int base; // base address of the parallel port // time(NULL) with microsecond resolution inline signed long long int get_time () { struct timeval ti; struct timezone tzp; gettimeofday (&ti, &tzp); return (ti.tv_usec + 1000000 * ((long long int) ti.tv_sec)); } void sig_handler_main (const int sig) // signal handler for main { if ((SIGINT == sig) or (SIGILL == sig) or (SIGKILL == sig) or (SIGSEGV == sig) or (SIGTERM == sig)) { (void) printf ("Signal %d, program exiting... \r\n", sig); exit (-sig); } iopl (0); /* no I/O permission */ return; } void sig_handler0 (const int sig) // signal handler for the control pin thread { static int retval; retval = -sig; if (SIGUSR1 == sig) { pthread_exit ((void *) &retval); } if ((SIGINT == sig) or (SIGILL == sig) or (SIGKILL == sig) or (SIGSEGV == sig) or (SIGTERM == sig)) { (void) printf ("Signal %d, program exiting... \r\n", sig); exit (retval); } return; } // control pins thread void * func_control (void *threadid) { int i; static int retval; // Without static the returned retval would always be zero. retval = 0; for (i = 0; i <= 0xff; i++) signal (i, sig_handler0); // control port at base+2: only bits 0-3 are used, bits 0, 1, and 3 (sum 1+2+8=11) are inverted value2 xor_eq 11; // inversion of bits 0, 1 and 3 value2 and_eq 0xf; // bit mask for bits 0-3, this also sets irq enable to off and the data pins to output // printf("toggling thread started, output pattern %x\n", value2); // loop for rectangle pulses from the control pins if (delay2 > 0) for (;;) // endless loop with < 1 % CPU load { outb (value2, base + 2); usleep (delay2); outb (11, base + 2); // set all controll pins to low usleep (delay2); } else for (;;) // approx. 200 kHz Hz { outb (value2, base + 2); outb (11, base); // set all controll pins to low } pthread_exit ((void *) &retval); } // status pins thread void * func_status (void *threadid) { int i, lastbit = 0, i_last_output_time = 0, sum0 = 0, sum1 = 0; static int retval; // Without static the returned retval would always be zero. // double sum = 0.5; // sum for pwm // average cyclus time in µs long long int counter, lli_last_counter = 0, last_time = 0, act_time = 0, lli_last_pulses = 0, lli_pulses = 0, lli_cyclt = 0; retval = 0; for (i = 0; i <= 0xff; i++) signal (i, sig_handler0); for (counter = 1;; counter++) // read forever { // evaluate only pin 15, bit 3 for statistical PWM estimation i = inb (base + 1); if (0 == counter) lastbit = i bitand 0x08; if (i bitand 0x08) // high { if (0 == lastbit) // rising edge detected, next periode { act_time = get_time (NULL); if (last_time) // after the start { lli_cyclt += act_time - last_time; // cycle time lli_pulses++; // valid pulse } last_time = act_time; } sum1++; lastbit = 1; } else { lastbit = 0; sum0++; } if ((i = time (NULL)) - i_last_output_time) // every second { if (i_last_output_time) { printf ("Input at Pin 13: Duty cycle: %f +- %f, Frequency: %f Hz, Sample frequency: %lld Hz.\n", sum1 / ((double) (sum0 + sum1)), 1. / (sum1 ? sum1 : sum0), 1000000 * (lli_pulses - lli_last_pulses) / ((double) lli_cyclt), counter - lli_last_counter); //printf ("cycletime: %lld, counts: %lld \n", lli_cyclt, lli_pulses - lli_last_pulses); } else { i_last_output_time = i; while (i_last_output_time == time (NULL));// go to the beginning of the next second i = time (NULL); } i_last_output_time = i; lli_last_counter = counter; lli_last_pulses = lli_pulses; lli_cyclt = 0; sum0 = 0; sum1 = 0; } if (delay3 > 0) // without waiting: approx. 200 kHz, 99 % CPU load usleep (delay3); } pthread_exit ((void *) &retval); } int main (int argc, char *argv[]) /* Caution: A wrong parameter value can cause serious damage! */ { int value; // bit pattern for the data pins (0 = off, 255 = all high) int delay; // duration of a pulse/leap in µs static pthread_attr_t attr0, attr1; // static void *statusp; // for pthread_return static pthread_t thread0, thread1; static int i, id0, id1, i_ret; if (7 != argc) { printf ("Usage: %s \n", argv[0]); printf (" \n"); printf ("