From: Frank Mori Hess Date: Fri, 5 Jan 2007 21:09:28 +0000 (+0000) Subject: Added gpct_pulse_generator demo. X-Git-Tag: v0_8_0~40 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=daf810a11058e366dcaf6952b62ec8d90575544f;p=comedilib.git Added gpct_pulse_generator demo. --- diff --git a/demo/Makefile.am b/demo/Makefile.am index d93ced8..33b1a6f 100644 --- a/demo/Makefile.am +++ b/demo/Makefile.am @@ -1,7 +1,7 @@ noinst_PROGRAMS = \ - antialias ao_waveform ao_mmap apply_cal choose_clock \ - choose_routing cmd dio eeprom_dump board_info \ + antialias ao_waveform ao_mmap apply_cal board_info choose_clock \ + choose_routing cmd dio eeprom_dump gpct_pulse_generator \ inp inpn insn ledclock mmap outp poll receiver select \ sender sigio sv tut1 tut2 @@ -23,6 +23,10 @@ apply_cal_SOURCES = apply_cal.c common.c apply_cal_CFLAGS = $(COMEDILIB_CFLAGS) apply_cal_LDADD = $(COMEDILIB_LIBS) +board_info_SOURCES = info.c common.c +board_info_CFLAGS = $(COMEDILIB_CFLAGS) +board_info_LDADD = $(COMEDILIB_LIBS) + choose_clock_SOURCES = choose_clock.c common.c choose_clock_CFLAGS = $(COMEDILIB_CFLAGS) choose_clock_LDADD = $(COMEDILIB_LIBS) @@ -43,9 +47,9 @@ eeprom_dump_SOURCES = eeprom_dump.c common.c eeprom_dump_CFLAGS = $(COMEDILIB_CFLAGS) eeprom_dump_LDADD = $(COMEDILIB_LIBS) -board_info_SOURCES = info.c common.c -board_info_CFLAGS = $(COMEDILIB_CFLAGS) -board_info_LDADD = $(COMEDILIB_LIBS) +gpct_pulse_generator_SOURCES = gpct_pulse_generator.c common.c +gpct_pulse_generator_CFLAGS = $(COMEDILIB_CFLAGS) +gpct_pulse_generator_LDADD = $(COMEDILIB_LIBS) inp_SOURCES = inp.c common.c inp_CFLAGS = $(COMEDILIB_CFLAGS) diff --git a/demo/README b/demo/README index b9e4438..8203b6c 100644 --- a/demo/README +++ b/demo/README @@ -60,6 +60,16 @@ eeprom_dump: Dumps the EEPROM of a card, if it has one. Useful for debugging devices/drivers. +gpct_pulse_generator: + Causes an NI general-purpose counter subdevice to produce a + continuous train of pulses on its output. The -F option specifies + the pulse period (as a frequency in Hertz), and the argument specifies + the pulse width (in nanoseconds). By default, the pulse width will + be set to half the pulse period. You may need to use the "dio" + and "choose_routing" demos to route the counter's output to + an output line that you can observe (for instance one of the PFI + lines). + inp: Simple input: Reads one sample from one channel on one subdevice. diff --git a/demo/gpct_pulse_generator.c b/demo/gpct_pulse_generator.c new file mode 100644 index 0000000..15099aa --- /dev/null +++ b/demo/gpct_pulse_generator.c @@ -0,0 +1,258 @@ +/* + * NI general-purpose counter example. Configures the counter to + * produce a continuous pulse train. The argument specifies the + * number of nanoseconds the output pulse should remain high. + * The example assumes the board has already been configured to + * route the output signal of the counter to an appropriate + * location (you may need to route it to a PFI output line + * for example). + * Part of Comedilib + * + * Copyright (c) 2007 Frank Mori Hess + * + * This file may be freely modified, distributed, and combined with + * other software, as long as proper attribution is given in the + * source code. + */ +/* + * Requirements: A board with a National Instruments general-purpose + * counter, and comedi driver version 0.7.74 or newer. + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "examples.h" + +/*FIXME: move helper functions to common.c so they can be used on other (mostly counter) examples*/ + +int arm(comedi_t *device, unsigned subdevice, lsampl_t source) +{ + comedi_insn insn; + lsampl_t data[2]; + int retval; + + memset(&insn, 0, sizeof(comedi_insn)); + insn.insn = INSN_CONFIG; + insn.subdev = subdevice; + insn.chanspec = 0; + insn.data = data; + insn.n = sizeof(data) / sizeof(data[0]); + data[0] = INSN_CONFIG_ARM; + data[1] = source; + + retval = comedi_do_insn(device, &insn); + if(retval < 0) + { + fprintf(stderr, "%s: error:\n", __FUNCTION__); + comedi_perror("comedi_do_insn"); + return retval; + } + return 0; +} + +/* This resets the count to zero and disarms the counter. The counter output + is set low. */ +int reset_counter(comedi_t *device, unsigned subdevice) +{ + comedi_insn insn; + lsampl_t data[1]; + int retval; + + memset(&insn, 0, sizeof(comedi_insn)); + insn.insn = INSN_CONFIG; + insn.subdev = subdevice; + insn.chanspec = 0; + insn.data = data; + insn.n = sizeof(data) / sizeof(data[0]); + data[0] = INSN_CONFIG_RESET; + + retval = comedi_do_insn(device, &insn); + if(retval < 0) + { + fprintf(stderr, "%s: error:\n", __FUNCTION__); + comedi_perror("comedi_do_insn"); + return retval; + } + return 0; +} + +int set_counter_mode(comedi_t *device, unsigned subdevice, lsampl_t mode_bits) +{ + comedi_insn insn; + lsampl_t data[2]; + int retval; + + memset(&insn, 0, sizeof(comedi_insn)); + insn.insn = INSN_CONFIG; + insn.subdev = subdevice; + insn.chanspec = 0; + insn.data = data; + insn.n = sizeof(data) / sizeof(data[0]); + data[0] = INSN_CONFIG_SET_COUNTER_MODE; + data[1] = mode_bits; + + retval = comedi_do_insn(device, &insn); + if(retval < 0) + { + fprintf(stderr, "%s: error:\n", __FUNCTION__); + comedi_perror("comedi_do_insn"); + return retval; + } + return 0; +} + +int set_clock_source(comedi_t *device, unsigned subdevice, lsampl_t clock, lsampl_t period_ns) +{ + comedi_insn insn; + lsampl_t data[3]; + int retval; + + memset(&insn, 0, sizeof(comedi_insn)); + insn.insn = INSN_CONFIG; + insn.subdev = subdevice; + insn.chanspec = 0; + insn.data = data; + insn.n = sizeof(data) / sizeof(data[0]); + data[0] = INSN_CONFIG_SET_CLOCK_SRC; + data[1] = clock; + data[2] = period_ns; + + retval = comedi_do_insn(device, &insn); + if(retval < 0) + { + fprintf(stderr, "%s: error:\n", __FUNCTION__); + comedi_perror("comedi_do_insn"); + return retval; + } + return 0; +} + +int set_gate_source(comedi_t *device, unsigned subdevice, lsampl_t gate_index, lsampl_t gate_source) +{ + comedi_insn insn; + lsampl_t data[3]; + int retval; + + memset(&insn, 0, sizeof(comedi_insn)); + insn.insn = INSN_CONFIG; + insn.subdev = subdevice; + insn.chanspec = 0; + insn.data = data; + insn.n = sizeof(data) / sizeof(data[0]); + data[0] = INSN_CONFIG_SET_GATE_SRC; + data[1] = gate_index; + data[2] = gate_source; + + retval = comedi_do_insn(device, &insn); + if(retval < 0) + { + fprintf(stderr, "%s: error:\n", __FUNCTION__); + comedi_perror("comedi_do_insn"); + return retval; + } + return 0; +} + +int ni_gpct_start_pulse_generator(comedi_t *device, unsigned subdevice, unsigned period_ns, unsigned up_time_ns) +{ + int retval; + lsampl_t counter_mode; + const unsigned clock_period_ns = 50; /* 20MHz clock */ + unsigned up_ticks, down_ticks; +/* +FIXME: +How is output initialized to known state for toggling (reset?) +*/ + retval = reset_counter(device, subdevice); + if(retval < 0) return retval; + + retval = set_gate_source(device, subdevice, 0, NI_GPCT_DISABLED_GATE_SELECT | CR_EDGE); + if(retval < 0) return retval; + retval = set_gate_source(device, subdevice, 1, NI_GPCT_DISABLED_GATE_SELECT | CR_EDGE); + if(retval < 0) + { + fprintf(stderr, "Failed to set second gate source. This is expected for older boards (e-series, etc.)\n" + "that don't have a second gate.\n"); + } + + counter_mode = NI_GPCT_COUNTING_MODE_NORMAL_BITS; + // toggle output on terminal count + counter_mode |= NI_GPCT_OUTPUT_TC_TOGGLE_BITS; + // load on terminal count + counter_mode |= NI_GPCT_LOADING_ON_TC_BIT; + // alternate the reload source between the load a and load b registers + counter_mode |= NI_GPCT_RELOAD_SOURCE_SWITCHING_BITS; + // count down + counter_mode |= NI_GPCT_COUNTING_DIRECTION_DOWN_BITS; + // initialize load source as load b register + counter_mode |= NI_GPCT_LOAD_B_SELECT_BIT; + retval = set_counter_mode(device, subdevice, counter_mode); + if(retval < 0) return retval; + + /* 20MHz clock */ + retval = set_clock_source(device, subdevice, NI_GPCT_TIMEBASE_1_CLOCK_SRC_BITS, clock_period_ns); + if(retval < 0) return retval; + + up_ticks = (up_time_ns + clock_period_ns / 2) / clock_period_ns; + down_ticks = (period_ns + clock_period_ns / 2) / clock_period_ns - up_ticks; + /* set initial counter value by writing to channel 0 */ + retval = comedi_data_write(device, subdevice, 0, 0, 0, down_ticks); + if(retval < 0) return retval; + /* set "load a" register to the number of clock ticks the counter output should remain low + by writing to channel 1. */ + comedi_data_write(device, subdevice, 1, 0, 0, down_ticks); + if(retval < 0) return retval; + /* set "load b" register to the number of clock ticks the counter output should remain high + by writing to channel 2 */ + comedi_data_write(device, subdevice, 2, 0, 0, up_ticks); + if(retval < 0) return retval; + + retval = arm(device, subdevice, NI_GPCT_ARM_IMMEDIATE); + if(retval < 0) return retval; + + return 0; +} + +int main(int argc, char *argv[]) +{ + comedi_t *device; + unsigned up_time; + unsigned period_ns; + int retval; + struct parsed_options options; + + init_parsed_options(&options); + options.value = -1.; + parse_options(&options, argc, argv); + period_ns = lrint(1e9 / options.freq); + if(options.value < 0.) + up_time = period_ns / 2; + else + up_time = lrint(options.value); + device = comedi_open(options.filename); + if(!device) + { + comedi_perror(options.filename); + exit(-1); + } + /*FIXME: check that device is counter */ + printf("Generating pulse train on subdevice %d.\n", options.subdevice); + printf("Period = %d ns.\n", period_ns); + printf("Up Time = %d ns.\n", up_time); + printf("Down Time = %d ns.\n", period_ns - up_time); + + retval = ni_gpct_start_pulse_generator(device, options.subdevice, period_ns, up_time); + if(retval < 0) return retval; + return 0; +} +