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.
This commit is contained in:
Bernd Porr 2012-05-03 00:39:04 +01:00
parent 74a1207a29
commit c22d498dc0
5 changed files with 365 additions and 1 deletions

View file

@ -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

View file

@ -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)

View file

@ -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 <ds@schleef.org> and Bernd Porr <berndporr@f2s.com>.

View file

@ -0,0 +1,316 @@
/*
This program reads information about a comedi device and
displays the information in a human-readable form.
*/
#include <stdio.h>
#include <comedilib.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
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<<n;
char *t=s;
for(;bit;bit>>=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;chan<n_chans;chan++){
printf(" chan%d: %lu\n",chan,
(unsigned long)comedi_get_maxdata(it,i,chan));
}
}
printf(" ranges:\n");
if(!comedi_range_is_chan_specific(it,i)){
n_ranges=comedi_get_n_ranges(it,i,0);
printf(" all chans:");
for(j=0;j<n_ranges;j++){
rng=comedi_get_range(it,i,0,j);
unit_to_desc(strtmp,rng->unit);
printf(" [%g%s,%g%s]",rng->min,strtmp,rng->max,strtmp);
}
printf("\n");
}else{
for(chan=0;chan<n_chans;chan++){
n_ranges=comedi_get_n_ranges(it,i,chan);
printf(" chan%d:",chan);
for(j=0;j<n_ranges;j++){
rng=comedi_get_range(it,i,chan,j);
unit_to_desc(strtmp,rng->unit);
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;
}

View file

@ -275,6 +275,7 @@ fi
AC_CONFIG_FILES(
Makefile
comedi_config/Makefile
comedi_board_info/Makefile
c++/Makefile
c++/include/Makefile
etc/Makefile