From: Bernd Porr Date: Wed, 2 May 2012 23:39:04 +0000 (+0100) Subject: Added comedi_board_info which has been inspired by the original X-Git-Url: http://git.tremily.us/?p=comedilib.git;a=commitdiff_plain;h=c22d498dc06f1f9bd3d3bb07ed98b1c92d789284 Added comedi_board_info which has been inspired by the original info in the demo directory. I've made it a bit more verbose and also took into account that now the generic_timed command takes more than one channel so that a user can experiment which sampling rates are possible with different channel numbers. I've also added an option to set the sampling rate for the generic_timed command. That sould actually not be needed but I'm sure there are drivers out there which do not correct the sampling rate downwards automatically. --- diff --git a/Makefile.am b/Makefile.am index 16f5207..f436177 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,5 +1,5 @@ -SUBDIRS = lib comedi_config man testing demo doc swig \ +SUBDIRS = lib comedi_config man testing demo comedi_board_info doc swig \ include etc scxi c++ pkgconfigdir = $(libdir)/pkgconfig diff --git a/comedi_board_info/Makefile.am b/comedi_board_info/Makefile.am new file mode 100644 index 0000000..c0b8c24 --- /dev/null +++ b/comedi_board_info/Makefile.am @@ -0,0 +1,7 @@ +bin_PROGRAMS = comedi_board_info + +man_MANS = comedi_board_info.1 + +comedi_board_info_SOURCES = comedi_board_info.c +comedi_board_info_CFLAGS = $(COMEDILIB_CFLAGS) +comedi_board_info_LDADD = $(COMEDILIB_LIBS) diff --git a/comedi_board_info/comedi_board_info.1 b/comedi_board_info/comedi_board_info.1 new file mode 100644 index 0000000..99a64ec --- /dev/null +++ b/comedi_board_info/comedi_board_info.1 @@ -0,0 +1,40 @@ +.TH COMEDI_BOARD_INFO "1" "May 2012" "comedi_board_info" "User Commands" +.SH NAME +comedi_board_info \- reports useful information about COMEDI devices +.SH SYNOPSIS +.B comedi_board_info +[\fIOPTIONS\fR] COMEDI_DEVICE +.SH DESCRIPTION +This program reads information about a comedi device and +displays it in a human-readable form. It also +probes the asynchronous acquisition by calling the function +probe_cmd_generic_timed +which provides information about the maximum sampling rate of +the board +and how the card acquires multiple channels (e.g. at once or +one by one). +.TP +\-n NUMBER_OF_CHANNELS +Requests the number of channels for the asynchronous acquisition. +This might influence the maximum sampling rate and the mode how +the comedi device samples the channels. +If the number is higher than the available +channels this will be corrected downwards to the number of available +channels. +.TP +\-F SAMPLING_RATE +Requests the sampling rate for an asynchronous +acquisition. By default this is set to comedi's highest sampling rate +so that the driver is forced to reduce it and will in most cases +return the maximum possible sampling rate. +.TP +\-v +Verbose output. +.TP +\-h +Shows a brief help screen. +.SH "SEE ALSO" +More documentation can be found on +http://www.comedi.org/ +.SH AUTHOR +comedi_board_info was written by David Schleef and Bernd Porr . diff --git a/comedi_board_info/comedi_board_info.c b/comedi_board_info/comedi_board_info.c new file mode 100644 index 0000000..bbe38ee --- /dev/null +++ b/comedi_board_info/comedi_board_info.c @@ -0,0 +1,316 @@ +/* + This program reads information about a comedi device and + displays the information in a human-readable form. + */ + +#include +#include +#include +#include +#include +#include +#include + + +static char * const default_filename = "/dev/comedi0"; + +int verbose = 0; + +static const char * const subdevice_types[]={ + "unused", + "analog input", + "analog output", + "digital input", + "digital output", + "digital I/O", + "counter", + "timer", + "memory", + "calibration", + "processor", + "serial digital I/O", + "pwm" +}; + +struct subdev_flag { + const char *sdf_define; + unsigned int bitmask; + const char *description; +}; + +static struct subdev_flag subdev_flags[] = { + {"SDF_MAXDATA",0x0010,"maxdata depends on channel"}, + {"SDF_FLAGS",0x0020,"flags depend on channel"}, + {"SDF_RANGETYPE",0x0040,"range type depends on channel"}, + {"SDF_MODE0",0x0080,"can do mode 0"}, + {"SDF_MODE1",0x0100,"can do mode 1"}, + {"SDF_MODE2",0x0200,"can do mode 2"}, + {"SDF_MODE3",0x0400,"can do mode 3"}, + {"SDF_MODE4",0x0800,"can do mode 4"}, + {"SDF_SOFT_CALIBRATED",0x2000,"subdevice uses software calibration"}, + {"SDF_CMD_WRITE",0x4000,"can do output commands"}, + {"SDF_CMD_READ",0x8000,"can do input commands"}, + {"SDF_READABLE",0x00010000,"subdevice can be read (e.g. analog input)"}, + {"SDF_WRITABLE",0x00020000,"subdevice can be written (e.g. analog output)"}, + {"SDF_INTERNAL",0x00040000,"subdevice does not have externally visible lines"}, + {"SDF_GROUND",0x00100000,"can do aref=ground"}, + {"SDF_COMMON",0x00200000,"can do aref=common"}, + {"SDF_DIFF",0x00400000,"aref=diff"}, + {"SDF_OTHER",0x00800000,"can do aref=other"}, + {"SDF_DITHER",0x01000000,"can do dithering"}, + {"SDF_DEGLITCH",0x02000000,"can do deglitching"}, + {"SDF_MMAP",0x04000000,"can do mmap()"}, + {"SDF_RUNNING",0x08000000,"subdevice is acquiring data"}, + {"SDF_LSAMPL",0x10000000,"subdevice uses 32-bit samples"}, + {"SDF_PACKED",0x20000000,"subdevice can do packed DIO"}, + {0,0,0}}; + +void explain_subdevice_flags(char* padding,unsigned int sf) { + int i = 0; + while (subdev_flags[i].sdf_define) { + if (sf & subdev_flags[i].bitmask) + printf("%s%s:%s\n", + padding, + subdev_flags[i].sdf_define, + subdev_flags[i].description); + i++; + } +} + +void unit_to_desc(char *udesc,int unit) { + switch(unit) { + case UNIT_volt: strcpy(udesc," V"); break; + case UNIT_mA: strcpy(udesc," mA"); break; + case UNIT_none: strcpy(udesc,""); break; + default: sprintf(udesc," (unknown unit %d)", + unit); + } +} + + + +char *tobinary(char *s,int bits,int n) +{ + int bit=1<>=1) + *t++=(bits&bit)?'1':'0'; + *t=0; + + return s; +} + + +char *cmd_src(int src,char *buf) +{ + buf[0]=0; + + if(src&TRIG_NONE)strcat(buf,"none|"); + if(src&TRIG_NOW)strcat(buf,"now|"); + if(src&TRIG_FOLLOW)strcat(buf, "follow|"); + if(src&TRIG_TIME)strcat(buf, "time|"); + if(src&TRIG_TIMER)strcat(buf, "timer|"); + if(src&TRIG_COUNT)strcat(buf, "count|"); + if(src&TRIG_EXT)strcat(buf, "ext|"); + if(src&TRIG_INT)strcat(buf, "int|"); +#ifdef TRIG_OTHER + if(src&TRIG_OTHER)strcat(buf, "other|"); +#endif + + if(strlen(buf)==0){ + sprintf(buf,"unknown(0x%08x)",src); + }else{ + buf[strlen(buf)-1]=0; + } + + return buf; +} + + + +void probe_cmd_generic_timed(comedi_t *it,int s,int n_channels,int freq_for_generic_timed) +{ + comedi_cmd cmd; + char buf[100]; + + printf(" command structure filled with probe_cmd_generic_timed for %d channels:\n", + n_channels); + if(comedi_get_cmd_generic_timed(it, s, &cmd, n_channels, 1E9/freq_for_generic_timed)<0){ + printf(" not supported\n"); + }else{ + printf(" start: %s %d\n", + cmd_src(cmd.start_src,buf),cmd.start_arg); + printf(" scan_begin: %s %d\n", + cmd_src(cmd.scan_begin_src,buf),cmd.scan_begin_arg); + if (verbose) { + if ((cmd.scan_begin_src == TRIG_TIMER)&&(cmd.scan_begin_arg)) { + printf(" scan_begin_src = TRIG_TIMER:\n" + " The sampling rate is defined per scan\n" + " meaning all channels are sampled at\n" + " the same time. The maximum sampling rate is f=%d Hz\n", + (int)(1E9/cmd.scan_begin_arg));} + } + printf(" convert: %s %d\n", + cmd_src(cmd.convert_src,buf),cmd.convert_arg); + if (verbose) { + if ((cmd.convert_src == TRIG_TIMER)&&(cmd.convert_arg)) { + printf(" convert_src = TRIG_TIMER\n" + " The sampling rate is defined per channel\n" + " meaning that a multiplexer is being switched from\n" + " channel to channel at a maximum rate of %d Hz.\n" + " The overall sampling rate needs to be divided\n" + " by the number of channels and results in f=%d Hz.\n", + (int)(1E9/cmd.convert_arg), + (int)(1E9/cmd.convert_arg/n_channels)); + } + } + printf(" scan_end: %s %d\n", + cmd_src(cmd.scan_end_src,buf),cmd.scan_end_arg); + printf(" stop: %s %d\n", + cmd_src(cmd.stop_src,buf),cmd.stop_arg); + } +} + + + +void get_command_stuff(comedi_t *it,int s,int n_chans_for_generic_timed,int freq_for_generic_timed) +{ + comedi_cmd cmd; + char buf[100]; + + if(comedi_get_cmd_src_mask(it,s,&cmd)<0){ + printf(" not supported\n"); + }else{ + printf(" start: %s\n",cmd_src(cmd.start_src,buf)); + if (cmd.start_src == TRIG_EXT) + printf(" cmd.start_src allows external trigger (TRIG_EXT)," + " for example from on input pin at the device.\n"); + printf(" scan_begin: %s\n",cmd_src(cmd.scan_begin_src,buf)); + printf(" convert: %s\n",cmd_src(cmd.convert_src,buf)); + printf(" scan_end: %s\n",cmd_src(cmd.scan_end_src,buf)); + printf(" stop: %s\n",cmd_src(cmd.stop_src,buf)); + + probe_cmd_generic_timed(it,s,n_chans_for_generic_timed,freq_for_generic_timed); + } +} + + + +int main(int argc,char *argv[]) +{ + int i,j; + int n_subdevices,type; + const char *type_str; + int chan,n_chans; + int n_ranges; + int subdev_flags; + comedi_range *rng; + comedi_t *it; + char *filename = default_filename; + char c; + char strtmp[16]; + int n_chans_for_generic_timed = 1; + int freq_for_generic_timed = 1E9; + + while (-1 != (c = getopt(argc, argv, "hvn:F:"))) { + switch (c) { + case 'n': + n_chans_for_generic_timed = strtoul(optarg, NULL, 0); + break; + case 'F': + freq_for_generic_timed = strtoul(optarg, NULL, 0); + break; + case 'v': + verbose++; + break; + case 'h': + default: + fprintf(stderr, + "usage: comedi_board_info [OPTIONS] COMEDI_DEVICE\n" + " -n number of channels for async command (default 1)\n" + " -F probing sampling rate for async command (default 1Ghz)\n" + " -v verbose output\n" + " -h this help screen\n"); + exit(1); + } + } + + if(optind < argc) { + filename = argv[optind]; + } + + it = comedi_open(filename); + if(!it){ + comedi_perror(filename); + exit(1); + } + + printf("overall info:\n"); + printf(" version code: 0x%06x\n", comedi_get_version_code(it)); + printf(" driver name: %s\n", comedi_get_driver_name(it)); + printf(" board name: %s\n", comedi_get_board_name(it)); + printf(" number of subdevices: %d\n", n_subdevices = comedi_get_n_subdevices(it)); + + for(i = 0; i < n_subdevices; i++){ + printf("subdevice %d:\n",i); + type = comedi_get_subdevice_type(it, i); + if(type < (int)(sizeof(subdevice_types) / sizeof(subdevice_types[0]))){ + type_str = subdevice_types[type]; + }else{ + type_str = "UNKNOWN"; + } + printf(" type: %d (%s)\n",type,type_str); + if(type==COMEDI_SUBD_UNUSED) + continue; + subdev_flags = comedi_get_subdevice_flags(it, i); + printf(" flags: 0x%08x\n",subdev_flags); + if (verbose) explain_subdevice_flags(" ",subdev_flags); + n_chans=comedi_get_n_channels(it,i); + printf(" number of channels: %d\n",n_chans); + if(!comedi_maxdata_is_chan_specific(it,i)){ + printf(" max data value: %lu\n", (unsigned long)comedi_get_maxdata(it,i,0)); + }else{ + printf(" max data value: (channel specific)\n"); + for(chan=0;chanunit); + printf(" [%g%s,%g%s]",rng->min,strtmp,rng->max,strtmp); + } + printf("\n"); + }else{ + for(chan=0;chanunit); + printf(" [%g%s,%g%s]",rng->min,strtmp,rng->max,strtmp); + } + printf("\n"); + } + } + printf(" command:\n"); + if (n_chans_for_generic_timed>n_chans) + n_chans_for_generic_timed = n_chans; + if (n_chans_for_generic_timed<1) + n_chans_for_generic_timed = 1; + if (freq_for_generic_timed > 1E9) + freq_for_generic_timed = 1E9; + if (freq_for_generic_timed < 1) + freq_for_generic_timed = 1; + get_command_stuff(it,i,n_chans_for_generic_timed,freq_for_generic_timed); + } + + return 0; +} + diff --git a/configure.ac b/configure.ac index 97c848e..ffb85a7 100644 --- a/configure.ac +++ b/configure.ac @@ -275,6 +275,7 @@ fi AC_CONFIG_FILES( Makefile comedi_config/Makefile +comedi_board_info/Makefile c++/Makefile c++/include/Makefile etc/Makefile