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:
parent
74a1207a29
commit
c22d498dc0
5 changed files with 365 additions and 1 deletions
|
@ -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
|
||||
|
|
7
comedi_board_info/Makefile.am
Normal file
7
comedi_board_info/Makefile.am
Normal 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)
|
40
comedi_board_info/comedi_board_info.1
Normal file
40
comedi_board_info/comedi_board_info.1
Normal 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>.
|
316
comedi_board_info/comedi_board_info.c
Normal file
316
comedi_board_info/comedi_board_info.c
Normal 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;
|
||||
}
|
||||
|
|
@ -275,6 +275,7 @@ fi
|
|||
AC_CONFIG_FILES(
|
||||
Makefile
|
||||
comedi_config/Makefile
|
||||
comedi_board_info/Makefile
|
||||
c++/Makefile
|
||||
c++/include/Makefile
|
||||
etc/Makefile
|
||||
|
|
Loading…
Add table
Reference in a new issue