/* sine_9bit.c 9 bit parallel port driver as user space program without RT (RTAI) for driving a pendulum by a sine AC voltage, with linear scaling of the output voltage. The ninths bit is for the four synchrously used control pins, because they are much weaker than a data pin. Parameter: Base port (cat /proc/ioports | grep parport), Frequency in Hz and relativ Amplitude in Procent (integer). CPU load with 2,93 GHz CPU: Much less than 1 %. Dr. Rolf Freitag 2011, License: Beerware license. */ #include #include // error codes #include // or #include // exit, atoi #include // getuid(), usleep #include // timeofday #include #include #include #include // use common floating point numbers/arithmetic #include // case with break because 97 % of the cases do have a preceding break #define CASE break;case #define DEFAULT break;default // Rounding with minimized quantization error #define mc_ROUND_int(x) ( ( (x) > 0. ) ? (int)((x) + 0.5) : -(int)(-(x) + 0.5) ) // limit variable b to range [(a), (c)], so that a <= b <= c #define mc_LIMIT(a, b, c) {if (b<(a)) b=(a); else if (b>(c)) b=(c);} static unsigned int ui_baseport, ui_baseport1; const int i_time_quant=10; // time quantum in Mikroseconds for PWM: 10 Mikroseconds // simpler Sinus long double sine (const long double ld_omega, const long double ld_time) { return (sinl (ld_omega *ld_time)); } // The input value is -1...+1 and gets converted to 0...9 // by *5, +4,5, rounding, limit (0, 9) int ad_ld (const long double ld_in) { int i_out =0; // Version with equal size DA intervalls. // long double ld_tmp = ld_in *5; // now the value is -5 ... 5 // ld_tmp += 4.5; // now the value is -0.5 ... 9.5 // i_out = mc_ROUND_int(ld_tmp); // rouding to the next integer // mc_LIMIT( 0, i_out, 9); // eleminate ruaway values caused by floating point operations // Version with a half size DA intervall at the ends of the AD region. long double ld_tmp = ld_in +1.0; // now the value is 0.0 ... 2.0 ld_tmp *= 4.5; // now the value is 0 ... 9 i_out = mc_ROUND_int(ld_tmp); // rouding to the next integer return (i_out); } // Output: The value 0...9 as 9 bit value. 0 for all pins low, 1 for one on, ... 9 for all on. // -1 as sentinel for AC zero, capacitive ground, -2 for all on. void out (const int i_value) { switch (i_value) { case 0: outl (0x000b0000, ui_baseport); // all data and control pins off, output mode, irq disable, 0 bits on outl (0x000400ff, ui_baseport1); // all data and controll pins on, 9 bits on CASE 1: outl (0x000b0001, ui_baseport); // data port pin 0 on outl (0x000b00ff, ui_baseport1); CASE 2: outl (0x000b0003, ui_baseport); outl (0x000b007f, ui_baseport1); CASE 3: outl (0x000b0007, ui_baseport); outl (0x000b003f, ui_baseport1); CASE 4: outl (0x000b000f, ui_baseport); outl (0x000b001f, ui_baseport1); CASE 5: outl (0x000b001f, ui_baseport); outl (0x000b000f, ui_baseport1); CASE 6: outl (0x000b003f, ui_baseport); outl (0x000b0007, ui_baseport1); CASE 7: outl (0x000b007f, ui_baseport); outl (0x000b0003, ui_baseport1); CASE 8: outl (0x000b00ff, ui_baseport); outl (0x000b0001, ui_baseport1); // data port pin 0 on CASE 9: case -2: outl (0x000400ff, ui_baseport); // all data and controll pins on, 9 bits on outl (0x000b0000, ui_baseport1); // all data and control pins off, output mode, irq disable, 0 bits on CASE -1: outl (0x000d00f0, ui_baseport); // set the average value (AC ground): 4 data and 2 control pins high, 4.5 bits on outl (0x000d00f0, ui_baseport1); // set the average value (AC ground): 4 data and 2 control pins high, 4.5 bits on DEFAULT: printf("Error: Invalid value %d for output.\n", i_value); exit (-1); break; } } // scale the output value i_value (from amplitude modulation) with i_amplitude/100 // by PWM. This is the only part of the endless loop with waiting, to do the PWM. void pwm_out (const int i_value, const int i_amplitude) { // int i=1; // first PWM part: Output of value if (0 != i_amplitude) // amplitude is not zero: output of the value { out (i_value); // set the value usleep ((i_amplitude *i_time_quant) -1); // on time, minus one µs for the out } // second part: Output of ground if (100 == i_amplitude) // if no modulation: Done, return return; out (-1); // set the average value (AC zero): 4 data and 2 control pins high usleep (((100 -i_amplitude) *i_time_quant) -1); // sleep minus one for the time needed for the output } int main (int argc, char *argv[]) { int i_value=0; // long double ld_freq=0; // frequency in Hz int i_amplitude=0; // Amplitude in percent static struct timeval s; // start time, for time offset long double ld_value=0; __u64 u64_time=0; // time in mikroseconds __u64 u64_time_offset=0; // time offset = start time in mikroseconds __u64 u64_u=0; long double ld_omega=0; // Angular frequency if (5 != argc) { printf ("Usage: %s (both integer, see e. g. cat /proc/ioports | grep parport), frequency (float, < 5e4 for 100 %% ampl.), amplitude in percentage (integer)\n", argv[0]); printf ("Example: %s 0xe800 0xec00 1.219 100\n", argv[0]); exit (-1); } if (geteuid () != 0) { printf ("\a\n\nError: $EUID==%d!=0 (you are not a superuser).\n\n", getuid ()); exit (-EPERM); } ui_baseport = (unsigned int) strtol (argv[1], (char **) NULL, 0); ui_baseport1 = (unsigned int) strtol (argv[2], (char **) NULL, 0); ld_freq = strtold (argv[3], (char **) NULL); mc_LIMIT (1e-6, ld_freq, 1e6); i_amplitude = (int) strtol (argv[4], (char **) NULL, 0); mc_LIMIT (0, i_amplitude, 100); ld_omega=2.0*M_PI*ld_freq; printf("Setting zeroth base port address 0x%x = %d, first base port address 0x%x = %d, %Lf Hz frequency and %d %% amplitude.\n", ui_baseport, ui_baseport, ui_baseport1, ui_baseport1, ld_freq, i_amplitude); iopl (3); // allows access to all I/O-Ports, ioperm doesen't work above the 0x3ff-Limit e. g. at PCI-Cards // start with one second AC ground out (-1); // set the average value (AC zero) usleep (1000000); for (u64_u=0;;u64_u++) // endless loop with a frequency of 1,000,000/i_time_quant *1/100, 1 kHz with i_time_quant == 10 { gettimeofday( &s, NULL ); u64_time = (__u64)s.tv_sec *1000000 +(__u64)s.tv_usec -u64_time_offset; if (0 == u64_u) u64_time_offset = u64_time; // get the time offset at the start of the endless loop ld_value = sine (ld_omega, (long double)u64_time *1E-6); i_value = ad_ld (ld_value); pwm_out (i_value, i_amplitude); } iopl (0); /* release region */ exit (0); }