/* * 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; }