Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
A
adc-lib
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
image/svg+xml
Discourse
Discourse
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Commits
Open sidebar
misc
adc-lib
Commits
c107af64
Commit
c107af64
authored
Oct 23, 2018
by
Federico Vaga
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'release/v2.2'
parents
f0f1224b
655c0b9f
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
1611 additions
and
432 deletions
+1611
-432
conf.py
doc/conf.py
+7
-2
library-api.rst
doc/library-api.rst
+1
-89
library-user.rst
doc/library-user.rst
+165
-152
Makefile
lib/Makefile
+1
-0
adc-genericfake.c
lib/adc-genericfake.c
+15
-0
adc-lib-100m14b4cha.h
lib/adc-lib-100m14b4cha.h
+14
-0
adc-lib-int.h
lib/adc-lib-int.h
+8
-0
adc-lib.h
lib/adc-lib.h
+61
-1
adc-ziofake.c
lib/adc-ziofake.c
+15
-0
fmc-adc-100m14b4cha.c
lib/fmc-adc-100m14b4cha.c
+552
-30
lib-math.c
lib/lib-math.c
+62
-0
lib.c
lib/lib.c
+100
-0
route.c
lib/route.c
+183
-5
adc-acq.c
tools/adc-acq.c
+427
-153
No files found.
doc/conf.py
View file @
c107af64
...
...
@@ -59,9 +59,9 @@ author = 'Federico Vaga <federico.vaga@cern.ch>'
# built documents.
#
# The short X.Y version.
version
=
'2.
1
'
version
=
'2.
2
'
# The full version, including alpha/beta/rc tags.
release
=
'2.
1
.0'
release
=
'2.
2
.0'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
...
...
@@ -201,4 +201,9 @@ breathe_projects = {
"adc-lib"
:
"doxygen-lib-output/xml/"
,
}
breathe_projects_source
=
{
"adc-lib"
:
(
"../lib/"
,
[
"adc-lib.h"
,
"route.c"
])
}
breathe_default_project
=
"adc-lib"
doc/library-api.rst
View file @
c107af64
The Library API
================
Enumerations And Constants
-----------------------------
.. doxygenenum:: adc_supported_boards
.. doxygenenum:: adc_configuration_type
.. doxygenenum:: adc_configuration_trigger_ext
.. doxygenenum:: adc_configuration_trigger_thr
.. doxygenenum:: adc_configuration_acquisition
.. doxygenenum:: adc_configuration_channel
.. doxygenenum:: adc_configuration_board
Board Specific
''''''''''''''
FMC ADC 100M 14 bit 4 Channel
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. doxygenenum:: adc_configuration_100m14b4cha
Data Structures
---------------
.. doxygenstruct:: adc_conf
:members:
.. doxygenstruct:: adc_buffer
:members:
.. doxygenstruct:: adc_timestamp
:members:
Functions
----------
.. doxygenfunction:: adc_init
.. doxygenfunction:: adc_exit
.. doxygenfunction:: adc_strerror
.. doxygenfunction:: adc_open
.. doxygenfunction:: adc_open_by_lun
.. doxygenfunction:: adc_close
.. doxygenfunction:: adc_apply_config
.. doxygenfunction:: adc_retrieve_config
.. doxygenfunction:: adc_set_conf_mask
.. doxygenfunction:: adc_set_conf_mask_all
.. doxygenfunction:: adc_set_conf
.. doxygenfunction:: adc_get_conf
.. doxygenfunction:: adc_reset_conf
.. doxygenfunction:: adc_get_capabilities
.. doxygenfunction:: adc_set_param
.. doxygenfunction:: adc_get_param
.. doxygenfunction:: adc_acq_start
.. doxygenfunction:: adc_acq_poll
.. doxygenfunction:: adc_acq_stop
.. doxygenfunction:: adc_request_buffer
.. doxygenfunction:: adc_release_buffer
.. doxygenfunction:: adc_fill_buffer
.. doxygenfunction:: adc_tstamp_buffer
.. doxygenfunction:: adc_trigger_fire
.. autodoxygenfile:: adc-lib.h
doc/library-user.rst
View file @
c107af64
...
...
@@ -23,19 +23,17 @@ Initialization and Cleanup
--------------------------
The library may keep internal information, so the application should
call its initialization function. After use, it should call the
exit function to release internal data, but it is not mandatory to
do that at program termination, because the operating system releases
anything in any case -- the library doesn't leave unexpected files
in persistent storage.
call its initialization function :cpp:func:`adc_init()`. After use,
it should call the exit function :cpp:func:`adc_exit()` to release
any internal data.
.. doxygenfunction:: adc_init
:outline:
.. note::
.. doxygenfunction:: adc_exit
:outline:
:cpp:func:`adc_exit()` is not mandatory, the operating system
releases anything in any case -- the library doesn't leave unexpected
files in persistent storage.
The functions don't do anything at this point, but they may be
The
se
functions don't do anything at this point, but they may be
implemented in later releases. For example, the library may scan the
system and cache the list of peripheral cards found, to make
later *open* calls faster. For this reason it is **recommended**
...
...
@@ -52,17 +50,14 @@ Error Reporting
Each library function returns values according to standard *libc*
conventions: -1 or NULL (for functions returning ``int`` or pointers,
resp.) is an error indication. When error happens, the
``errno`
`
resp.) is an error indication. When error happens, the
:manpage:`errno
`
variable is set appropriately.
The
``errno`
` values can be standard Posix items like ``EINVAL``, or
The
:manpage:`errno
` values can be standard Posix items like ``EINVAL``, or
library-specific values, for example ``ADC_ENOSET`` (*Cannot set requested
item*). All library-specific error values have a value greater than
1024, to prevent collision with standard values. To convert
such values to a string please use the function:
.. doxygenfunction:: adc_strerror
:outline:
such values to a string please use :cpp:func:`adc_strerror()`
Following an example from the ``example.c`` code available under ``tools``
...
...
@@ -74,21 +69,16 @@ Following an example from the ``example.c`` code available under ``tools``
Opening and closing
--------------------
Each device must be opened before use, and it should be closed after
use. It is not mandatory, but it is recommended, to close if the process
is going to terminate, as the library has no persistent storage to clean
up -- but there may be persistent buffer storage allocated, and
*adc_close* may release it in future versions.
.. doxygenfunction:: adc_open
:outline:
.. doxygenfunction:: adc_close
:outline:
Each device must be opened before use by calling :cpp:func:`adc_open()`,
and it should be closed after use by calling :cpp:func:`adc_close()`.
..
doxygendefine:: ADC_F_FLUSH
..
note ::
.. doxygendefine:: ADC_F_VERBOSE
:cpp:func:`adc_close()` is not mandatory, but it is recommended, to
close if the process is going to terminate, as the library has no
persistent storage to clean up -- but there may be persistent buffer
storage allocated, and :cpp:func:`adc_close()` may release it in
future versions.
Following an example from the ``example.c`` code available under ``tools``
...
...
@@ -96,9 +86,11 @@ Following an example from the ``example.c`` code available under ``tools``
:language: c
:lines: 65-74
As you can see in the example the ``adc_open`` parameters ``totalsamples``
and ``nbuffer`` are optional. The purpose of these parameters is to give
an hint to the library about the resources that the user will ask for.
As you can see in the example the :cpp:func:`adc_open()` parameters
``totalsamples`` and ``nbuffer`` are optional. The purpose of these
parameters is to give an hint to the library about the resources that
the user will ask for.
Managing big acquisitions (hundreds of megabytes, or gigabytes) requires
some pre-allocation of the data, and sometimes this configuration happens
at device level, so it's good to have the information at open time. On
...
...
@@ -106,19 +98,15 @@ the other hand a multi-shot acquisition requires all buffers to be available
at the same time and also this may require some pre-allocation at the device
level.
The data structure returned by
``adc_open`` is an opaque pointer used as token
to access the API functions. The user is not supposed to use or modify this
pointer.
The data structure returned by
:cpp:func:`adc_open()` is an opaque pointer
used as token to access the API functions. The user is not supposed to use
or modify this
pointer.
Another kind of open function has been provided to satisfy CERN's developers
needs. This is the open by LUN (*Logic Unit Number*); here the LUN concept
reflects the *CERN BE-CO* one.
.. doxygenfunction:: adc_open_by_lun
:outline:
The usage is exactly the same as ``adc_open`` only that it uses the LUN
instead of the device ID.
needs. Function :cpp:func:`adc_open_by_lun()` is the open by LUN
(*Logic Unit Number*); here the LUN concept reflects the *CERN BE-CO* one.
The usage is exactly the same as :cpp:func:`adc_open()` only that it uses
the LUN instead of the device ID.
Configuration
...
...
@@ -131,10 +119,8 @@ configuration is something specific to each board.
Configuration is performed by passing parameters as 32-bit numbers.
The library defines arrays of such parameters, one for each
aspect of the overall problem (triggers, data, and so on).
.. doxygenenum:: adc_configuration_type
:outline:
aspect of the overall problem (triggers, data, and so on)
:cpp:type:`adc_configuration_type`.
Each item in the array has a symbolic name, and each array is associated
with a bit-mask that specifies which parameters have been set.
...
...
@@ -142,30 +128,19 @@ The choice of arrays is driven by the need for generic structures
that can be used unchanged with different hardware cards.
The position of each parameter in the array is fixed, and as boards are added
to this library we may need to add new values for unforeseen requirements.
For each type in :cpp:type:`adc_configuration_type` there is a set of
defined options: :cpp:type:`adc_configuration_board`,
:cpp:type:`adc_configuration_channel`,
:cpp:type:`adc_configuration_acquisition`,
:cpp:type:`adc_configuration_custom`,
:cpp:type:`adc_configuration_trigger_ext`,
:cpp:type:`adc_configuration_trigger_thr`,
:cpp:type:`adc_configuration_trigger_tim`.
.. warning:: Rembember that some of the following options may have
device-specific meaning.
.. doxygenenum:: adc_configuration_board
:outline:
.. doxygenenum:: adc_configuration_channel
:outline:
.. doxygenenum:: adc_configuration_acquisition
:outline:
.. doxygenenum:: adc_trigger_polarity
:outline:
.. doxygenenum:: adc_configuration_trigger_ext
:outline:
.. warning:: Rembember that most options have device-specific meaning.
When unsure, refer to the driver manual for their meaning.
.. doxygenenum:: adc_configuration_trigger_thr
:outline:
.. doxygenenum:: adc_configuration_trigger_tim
:outline:
:cpp:type:`adc_trigger_polarity`
While there is a little overhead in parsing the generic structures,
configuration is not something that happens in hot paths, and we see
...
...
@@ -173,39 +148,30 @@ no problems in that. Also, please note that setting the configuration
and applying it are different steps, and the configuration data structures
can be prepared and saved for later use.
.. doxygenstruct:: adc_conf
.. doxygenfunction:: adc_apply_config
:outline:
.. doxygenfunction:: adc_retrieve_config
:outline:
The users are free to access the ``adc_conf`` structure to set up the
Users are free to access the :cpp:type:`adc_conf` structure to set up the
configuration. To help a bit this configuration process, especially when
it is done dynamically, the library offers some helpers to *touch* this
data structure. The main problem that the following functions solve is the
fact that for eack value you have also to set a bit in the configuration
bitmask. If this is not done correctly the result can be unpredictable.
.. doxygenfunction:: adc_set_conf_mask
:outline:
- :cpp:func:`adc_set_conf_mask()`
- :cpp:func:`adc_set_conf_mask_all()`
.. doxygenfunction:: adc_set_conf_mask_all
:outline:
- :cpp:func:`adc_set_conf()`
.. doxygenfunction:: adc_set_conf
:outline:
- :cpp:func:`adc_get_conf()`
.. doxygenfunction:: adc_get_conf
:outline:
- :cpp:func:`adc_reset_conf()`
.. doxygenfunction:: adc_reset_conf
:outline:
- :cpp:func:`adc_get_capabilities()`
.. doxygenfunction:: adc_get_capabilities
:outline:
Once the configuration structure :cpp:type:`adc_conf` is can be sent
to the hardware with :cpp:func:`adc_apply_config()`. The hardware configuration
can be read back with :cpp:func:`adc_retrieve_config()`. If one of these
functions fail while configuring some values, then the flag
:cpp:any:`ADC_CONF_F_ERROR` will be set in :cpp:member:`adc_conf::flags`
Following an example from the ``example.c`` code available under ``tools``
...
...
@@ -219,18 +185,17 @@ Following an example from the ``example.c`` code available under ``tools``
:language: c
:lines: 186-203
There are also the correspondent functions to set/get more than one
configuration at time:
:cpp:func:`adc_apply_config_n()`, :cpp:func:`adc_retrieve_config_n()`.
To handle the exceptional cases where parameters are actually *strings*
(``char *``) or the library does not support them (e.g. board-specific
parameter), the library added the support to single access parameters.
The parameter's values can be both a *string* (``char *``) or a
*number* (``int``). An higher priority is given to a *string* when both are
asked at the same time.
.. doxygenfunction:: adc_set_param
:outline:
.. doxygenfunction:: adc_get_param
:outline:
*number* (``int``). User can use set values with :cpp:func:`adc_set_param()`
and get values with :cpp:func:`adc_get_param()`
::
...
...
@@ -250,15 +215,50 @@ asked at the same time.
err = adc_get_param(adc, "identifier", NULL, &v_int);
.. _`lib:usr:cfg:ac`:
Offset Auto Clear
'''''''''''''''''
It may happen that due to temperature or other external factors there are
constant offsets on channels. This library, through function
:cpp:func:`adc_offset_auto_clear()` offers a mechanism to be able
to compensate these offsets.::
err = adc_offset_auto_clear(adc, 0x0);
During this procedure, by default, the library will disconnect any input
signal and connect the channel to the local ground level. By using the flag
:cpp:any:`ADC_OFFSET_AC_F_MANUAL` it is possible to use the current input signal
and compensate it.
During this process, the library may change the ADC configuration.
It is possible to ask the library to restore any previous configuration by
using the flag :cpp:any:`ADC_OFFSET_AC_F_RESTORE`.
If this feature is not supported in hardware or in the driver, it is possible
to ask for a software implementation by using the flag
:cpp:any:`ADC_OFFSET_AC_F_SOFTWARE`. In this case the idea is to run some
acquisitions, compute the average and apply this value as offset compensation.
If on a particular board the offset auto-clear needs extra parameters, you
have to set them using :ref:`board specific configurations<lib:usr:cfg:cus>`.
.. warning:: For some boards, once this offset has been configured and
applied, it will stay as long as the ADC *session* is alive. This means
that on :cpp:func:`adc_close()` the configuration is lost.
.. _`lib:usr:cfg:cus`:
Board Specific Configuration
''''''''''''''''''''''''''''
The library tries to be generic and at the same time being able to satisfy most
of the needs, but sometimes it fails in this and some board-specific
configuration is necessary. This is the pupose of the
``ADC_CONF_TYPE_CUS``
configuration type. When a board has some specificity it shall provide a
dedicated header in order to export them; this shall include an ``enum``
describing the configuration values.
configuration is necessary. This is the pupose of the
:cpp:any:`ADC_CONF_TYPE_CUS` configuration type. When a board has some
specificity it shall provide a dedicated header in order to export them;
this shall include an ``enum``
describing the configuration values.
::
...
...
@@ -280,39 +280,49 @@ describing the configuration values.
err = adc_get_conf(&cfg, ADC_SPECIAL_CUS_OPT3, &opt3);
As described above, for a board specific configuration it is reccomended to use
the functions
``adc_set_param`` and ``adc_get_param``. But anyway, the library
includes dedicated header files for each board type that exports helpers for
the most common parameters.
the functions
:cpp:func:`adc_set_param()` and :cpp:func:`adc_get_param()`.
But anyway, the library includes dedicated header files for each board type
th
at exports helpers for th
e most common parameters.
FMC ADC 100M 14 bit 4 Channel
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This card has some parameters which are not described in the generic API. In
order to use them you shall include the header file ``fmc-adc-100m14b4cha.h``.
Here you can get the dedicated symbols for the configuration.
.. doxygenenum:: adc_configuration_100m14b4cha
Here you can get the dedicated symbols for the custom configuration options
:cpp:type:`adc_configuration_100m14b4cha`
Today, within this context, the ``route_to`` options does not have any meaning
since the specifc parameters for this card are global, so there is no need for
any routing mechanism.
Following some special values defined for this board.
.. doxygenenum:: adc_configuration_100m14b4cha_channel_range
The :ref:`zero offset<lib:usr:cfg:ac>` value is valid only within a *session*.
Every time you invoke :cpp:func:`adc_open()` this particular offset get
reset.
Acquisition
-----------
As we already said, the `configuration`_ is the most important step in the
acquisition process. Once the card is configured, the actual management of an
acquisition is limited to very few operations:
*start*, *stop* and *poll*.
acquisition is limited to very few operations:
.. doxygenfunction:: adc_acq_start
:outline:
*start*
The ADC starts acquiring data. Depending on the board, this may means
that the board is waiting for a trigger event to happen.
The user can start the acquisition with :cpp:func:`adc_acq_start()`
.. doxygenfunction:: adc_acq_poll
:outline:
*stop*
The ADC stops an on going acquisition. The user can stop the acquisition
with :cpp:func:`adc_acq_stop()`
.. doxygenfunction:: adc_acq_stop
:outline:
*poll*
The user can check if any data is ready to be fetched. The user can use
a special poll function :cpp:func:`adc_acq_poll()`
The full acquisition flow is represented in the following picture
...
...
@@ -343,7 +353,8 @@ Following an example from the ``example.c`` code available under ``tools``
Buffers
-------
Each acquisition run is relying on a buffer. The buffer includes both
Each acquisition run is relying on a buffer. Buffers are described
by the data structure :cpp:type:`adc_buffer`. This includes both
*data* and *meta-data*. The layout of data is depending on the
card and its configuration; the meta-data part depends on the type of
driver currently running for the card. For example, if the driver is
...
...
@@ -352,24 +363,16 @@ to a ZIO control block. The idea is that all information is available
to the user, who can use it at will or ignore it and just use generic
interfaces.
.. doxygenstruct:: adc_buffer
:members:
Buffers are generally allocated by the library, but the application
can provide its own allocator if it really wants to. The reason for
using library-driven allocation is that the library knows better. If,
for example, the driver offers *mmap* support, the library can
choose to map acquired data instead of calling *malloc*. However,
for the rare case where the application knows better, it can override
this. Also, the application should tell the number of buffers and
expected data size at the beginning, so the library can properly
configure the underlying driver, if this is needed.
.. doxygenfunction:: adc_request_buffer
:outline:
.. doxygenfunction:: adc_release_buffer
:outline:
Buffers are generally allocated by the library with
:cpp:func:`adc_request_buffer()` and released with
:cpp:func:`adc_release_buffer()`, but the application can provide its
own allocator if it really wants to. The reason for using library-driven
allocation is that the library knows better. If, for example, the driver
offers *mmap* support, the library can choose to map acquired data
instead of calling *malloc*. However, for the rare case where
the application knows better, it can override this. Also, the application
should tell the number of buffers and expected data size at the beginning,
so the library can properly configure the underlying driver, if this is needed.
By accepting a user-defined allocator, we allow customized management
of the data area in the most flexible way. The application may have
...
...
@@ -391,13 +394,10 @@ samples in the buffer may be bigger than the number of samples that will
actually be acquired later.
Once an acquisition successfully complete it is possible to fill the buffer
with the acquisition data and its meta-data (if any).
.. doxygenfunction:: adc_fill_buffer
:outline:
.. doxygenfunction:: adc_tstamp_buffer
:outline:
with the acquisition data and its meta-data (if any) with
:cpp:func:`adc_fill_buffer()`. It is also possible to extract
the acquisition timestamp :cpp:type:`adc_timestamp` with
:cpp:func:`adc_tstamp_buffer()`
Following an example from the ``example.c`` code available under ``tools``
...
...
@@ -433,12 +433,29 @@ for most of them the extraction is driver-specific.
The details about the actual content of ``zio_control`` is described
in the driver documentation.
Fixup
'''''
It may happen that low-level (HDL, hardware) bugs are found and they cannot
be fixed. This library offers the possibility to compensate for those errors.
For each type of board we can have, or not, a *fixup* algorithm that is able
to fix the data in the buffer and compensate the bugs.
There two possibilities to run the *fixup* algorithm over a given buffer.
The first one is to pass the flag :cpp:any:`ADC_F_FIXUP` to
:cpp:func:`adc_fill_buffer()`.::
err = adc_fill_buffer(adc, buff, ADC_F_FIXUP, &timeout);
The second one is to run it explicitly by invoking
:cpp:func:`adc_buffer_fixup()`.::
err = adc_buffer_fixup(buf);
Time Stamps
-------------
The time-stamp structure is defined as follows:
.. doxygenstruct:: adc_timestamp
The time-stamp structure is defined by :cpp:type:`adc_timestamp`
This is the same structure as used by the `ZIO framework`_, but it is not
specific to ZIO -- the choice made there was just the best of breed,
...
...
@@ -446,10 +463,7 @@ agreed upon in a discussion held at CERN within the BE-CO-HT section.
Currently, time-stamps are only used in association with buffers: after
an acquisition is over and saved to a buffer, the user can ask for
the time-stamp of the acquired buffers.
.. doxygenfunction:: adc_tstamp_buffer
:outline:
Following an example from the ``example.c`` code available under ``tools``
...
...
@@ -464,11 +478,10 @@ The software trigger is something that people may need for testing and many
cards provide support for it. Since this is pure software, there is no need
for this library to handle the configuration which is left to its users.
The user will define all the triggering criteria and then it will use this
library to actually do the trigger.
.. doxygenfunction:: adc_has_trigger_fire
library to actually do the trigger by invoking :cpp:func:`adc_trigger_fire()`.
.. doxygenfunction:: adc_trigger_fire
It is also possible to check if software triggering is actually supported
by the board in use by calling :cpp:func:`adc_has_trigger_fire()`.
::
...
...
lib/Makefile
View file @
c107af64
...
...
@@ -23,6 +23,7 @@ LOBJ += init.o
LOBJ
+=
config-zio.o
LOBJ
+=
buffer-zio.o
LOBJ
+=
lib.o
LOBJ
+=
lib-math.o
LOBJ
+=
adc-ziofake.o
LOBJ
+=
adc-genericfake.o
LOBJ
+=
adc-zio.o
...
...
lib/adc-genericfake.c
View file @
c107af64
...
...
@@ -330,6 +330,20 @@ static int adc_genfake_fill_buffer(struct adc_dev *dev,
return
0
;
}
static
int
adc_genfake_buffer_get_sample
(
struct
adc_buffer
*
buf
,
unsigned
int
chan
,
unsigned
int
acq_sample
,
int32_t
*
value
)
{
if
(
chan
>=
1
)
{
errno
=
EINVAL
;
return
-
1
;
}
*
value
=
((
int8_t
*
)
buf
->
data
)[
acq_sample
];
return
0
;
}
static
struct
adc_operations
fa_generic_fake_op
=
{
.
open
=
adc_genfake_open
,
...
...
@@ -349,6 +363,7 @@ static struct adc_operations fa_generic_fake_op = {
.
fill_buffer
=
adc_genfake_fill_buffer
,
.
tstamp_buffer
=
adc_genfake_tstamp_buffer
,
.
release_buffer
=
adc_genfake_release_buffer
,
.
buffer_get_sample
=
adc_genfake_buffer_get_sample
,
};
#define ADC_GENERICFAKE_BRD_MASK (1LL << ADC_CONF_BRD_N_CHAN)
...
...
lib/adc-lib-100m14b4cha.h
View file @
c107af64
...
...
@@ -23,6 +23,20 @@ extern "C" {
#include "adc-lib.h"
#define ADC_CONF_100M14B4CHA_CHN_RANGE_N 3
/**
* List of known voltage ranges to be used with the configuration option
* ADC_CONF_CHN_RANGE
*/
enum
adc_configuration_100m14b4cha_channel_range
{
ADC_CONF_100M14B4CHA_CHN_RANGE_OPEN_DRAIN
=
0
,
ADC_CONF_100M14B4CHA_CHN_RANGE_100mV
=
0x23
,
ADC_CONF_100M14B4CHA_CHN_RANGE_1V
=
0x11
,
ADC_CONF_100M14B4CHA_CHN_RANGE_10V
=
0x45
,
ADC_CONF_100M14B4CHA_CHN_RANGE_100mV_CAL
=
0x42
,
ADC_CONF_100M14B4CHA_CHN_RANGE_1V_CAL
=
0x40
,
ADC_CONF_100M14B4CHA_CHN_RANGE_10V_CAL
=
0x44
,
};
/**
* List of possible buffer types (options for ADC_CONF_100M14B4CHA_BUF_TYPE)
...
...
lib/adc-lib-int.h
View file @
c107af64
...
...
@@ -45,6 +45,9 @@ struct adc_operations {
typeof
(
adc_release_buffer
)
*
release_buffer
;
/**< @related adc_release_buffer */
typeof
(
adc_trigger_fire
)
*
trigger_fire
;
/**< @related adc_trigger_fire */
typeof
(
adc_buffer_get_sample
)
*
buffer_get_sample
;
/**< @related adc_buffer_get_sample */
typeof
(
adc_buffer_fixup
)
*
buffer_fixup
;
/**< @related adc_buffer_fixup*/
typeof
(
adc_offset_auto_clear
)
*
offset_auto_clear
;
/**< @related adc_offset_auto_clear */
};
...
...
@@ -88,6 +91,7 @@ struct __adc_dev_zio {
char
*
sysbase
;
/**< base path to device sysfs */
unsigned
long
samplesize
;
/**< size of 1 sample */
unsigned
long
pagesize
;
/**< size of 1 page */
void
*
priv
;
/**< specific ZIO board data */
/* Mandatory field */
struct
adc_gid
gid
;
/**< general ADC descriptor */
};
...
...
@@ -181,6 +185,10 @@ int adc_zio_get_param(struct adc_dev *dev, char *name,
int
adc_zio_sysfs_set
(
struct
__adc_dev_zio
*
fa
,
char
*
name
,
uint32_t
*
value
);
int
adc_offset_auto_clear_sw_avg
(
struct
adc_dev
*
dev
,
unsigned
long
flags
,
int32_t
*
offset
);
/*adc-genericfake*/
...
...
lib/adc-lib.h
View file @
c107af64
...
...
@@ -26,6 +26,12 @@ extern "C" {
#define ADC_ENOMASK 1030
#define ADC_EDISABLED 1031
#define ADC_EROUTE 1032
#define ADC_ENOP_SWTRG 1033
#define ADC_ENOP_OFFCLR 1034
#define ADC_ENOP_OFFCLRHW 1035
#define ADC_ENOP_OFFCLRSW 1036
#define ADC_OFF_AC_RESTORE_S 1037
#define ADC_OFF_AC_RESTORE_R 1038
/**
* Opaque type. any instance of this should be used as token
...
...
@@ -64,6 +70,7 @@ struct adc_buffer {
void
*
mapaddr
;
/**< mmap address */
unsigned
long
maplen
;
/**< mmap length */
unsigned
long
flags
;
/**< internal to the library */
void
*
priv
;
/**< library private date */
};
...
...
@@ -242,6 +249,13 @@ enum adc_configuration_type {
#define __ADC_CONF_LEN 64
/* number of allocated items in each structure */
/**
* It enumerates all possible flags for adc_conf.flags
*/
enum
adc_conf_flags
{
ADC_CONF_F_ERROR
=
(
1ULL
<<
0
),
/**< configuration failed */
};
/**
* ADC configuration descriptor.
*/
...
...
@@ -250,7 +264,7 @@ struct adc_conf {
uint32_t
dev_type
;
/**< device type */
uint32_t
route_to
;
/**< internal route to a particular sub-device
(e.g. a channel) */
uint32_t
flags
;
/**<
FIXME how to identify invalid?
*/
uint32_t
flags
;
/**<
flags from adc_conf_flags
*/
uint64_t
mask
;
/**< capabilities mask, 1 when the correspondent
value in ``value[]`` is valid */
uint32_t
value
[
__ADC_CONF_LEN
];
/**< array of configuration value.
...
...
@@ -265,6 +279,30 @@ struct adc_conf {
(used by adc_open) */
#define ADC_F_VERBOSE 0x00020000
/**< Flag used to verbose on stdout/stderr
(usable by any function)*/
#define ADC_F_FIXUP 0x00400000
/**< Flag used to fixup a buffer when
filling it (usable by adc_fill_buffer) */
/**
* Enumeration of all possible flags to driver the auto-clear offset
*/
enum
adc_offset_auto_clear_flags
{
ADC_OFFSET_AC_F_MANUAL
=
0x00000001
,
/**< the signal will be acquired with
the last configuration set */
ADC_OFFSET_AC_F_RESTORE
=
0x00000002
,
/**< restore previous
configuration when done. It
does not have any effect when
MANUAL is active*/
ADC_OFFSET_AC_F_SOFTWARE
=
0x00000004
,
/**< use software mechanism,
it implies manual
configuration */
ADC_OFFSET_AC_F_ZERO
=
0x00000008
,
/**< it sets the zero-offset to zero;
which means that it removes any
previously set zero-offset. */
__ADC_OFFSET_AC_F_MASK
=
(
ADC_OFFSET_AC_F_MANUAL
|
ADC_OFFSET_AC_F_RESTORE
|
ADC_OFFSET_AC_F_SOFTWARE
|
ADC_OFFSET_AC_F_ZERO
),
/**< used internally */
};
/**
* @defgroup dev Basic
...
...
@@ -287,6 +325,8 @@ extern struct adc_dev *adc_open_by_lun(char *name, int lun,
extern
int
adc_close
(
struct
adc_dev
*
dev
);
extern
int
adc_trigger_fire
(
struct
adc_dev
*
dev
);
extern
int
adc_has_trigger_fire
(
struct
adc_dev
*
dev
);
extern
int
adc_offset_auto_clear
(
struct
adc_dev
*
dev
,
unsigned
long
flags
);
/**@}*/
...
...
@@ -357,8 +397,13 @@ extern int adc_reset_conf(struct adc_dev *dev, unsigned int flags,
struct
adc_conf
*
conf
);
extern
int
adc_apply_config
(
struct
adc_dev
*
dev
,
unsigned
int
flags
,
struct
adc_conf
*
conf
);
extern
int
adc_apply_config_n
(
struct
adc_dev
*
dev
,
unsigned
int
flags
,
struct
adc_conf
*
conf
,
unsigned
int
n
);
extern
int
adc_retrieve_config
(
struct
adc_dev
*
dev
,
struct
adc_conf
*
conf
);
extern
int
adc_retrieve_config_n
(
struct
adc_dev
*
dev
,
struct
adc_conf
*
conf
,
unsigned
int
n
);
extern
uint64_t
adc_get_capabilities
(
struct
adc_dev
*
dev
,
enum
adc_configuration_type
type
);
extern
int
adc_get_param
(
struct
adc_dev
*
dev
,
char
*
name
,
...
...
@@ -396,6 +441,21 @@ extern struct adc_timestamp *adc_tstamp_buffer(struct adc_buffer *buf,
extern
int
adc_release_buffer
(
struct
adc_dev
*
dev
,
struct
adc_buffer
*
buf
,
void
(
*
free_fn
)(
void
*
));
extern
int
adc_buffer_get_sample
(
struct
adc_buffer
*
buf
,
unsigned
int
chan
,
unsigned
int
acq_sample
,
int32_t
*
value
);
extern
int
adc_buffer_fixup
(
struct
adc_buffer
*
buf
);
/**@}*/
/**
* @defgroup buf_math Buffer Math
* Mathematical operations on buffers
* @{
*/
extern
int
adc_buffer_math_avg
(
struct
adc_buffer
*
buf
,
unsigned
int
chan
,
int32_t
*
avg
);
/**@}*/
/* libfmcadc version string */
...
...
lib/adc-ziofake.c
View file @
c107af64
...
...
@@ -34,6 +34,20 @@ static struct adc_dev *adc_ziofake_open(const struct adc_board_type *b,
return
dev
;
}
static
int
adc_ziofake_buffer_get_sample
(
struct
adc_buffer
*
buf
,
unsigned
int
chan
,
unsigned
int
acq_sample
,
int32_t
*
value
)
{
if
(
chan
>=
1
)
{
errno
=
EINVAL
;
return
-
1
;
}
*
value
=
((
int8_t
*
)
buf
->
data
)[
acq_sample
];
return
0
;
}
#define ADC_ZIO_ACQ_MASK (1LL << ADC_CONF_ACQ_N_SHOTS) | \
(1LL << ADC_CONF_ACQ_POST_SAMP) | \
...
...
@@ -62,6 +76,7 @@ static struct adc_operations fa_zio_fake_op = {
.
fill_buffer
=
adc_zio_fill_buffer
,
.
tstamp_buffer
=
adc_zio_tstamp_buffer
,
.
release_buffer
=
adc_zio_release_buffer
,
.
buffer_get_sample
=
adc_ziofake_buffer_get_sample
,
};
struct
adc_board_type
adc_ziofake
=
{
...
...
lib/fmc-adc-100m14b4cha.c
View file @
c107af64
...
...
@@ -36,11 +36,54 @@
#define ADC_CONF_GET 0
#define ADC_CONF_SET 1
#define __ADC_CONF_CHN_OFFSET_ZERO 200
#define ADC_100M_4CH_14BIT_ACQ_MASK (1LL << ADC_CONF_ACQ_N_SHOTS) | \
(1LL << ADC_CONF_ACQ_POST_SAMP) | \
(1LL << ADC_CONF_ACQ_PRE_SAMP) | \
(1LL << ADC_CONF_ACQ_UNDERSAMPLE) | \
(1LL << ADC_CONF_ACQ_FREQ_HZ) | \
(1LL << ADC_CONF_ACQ_N_BITS)
#define ADC_100M_4CH_14BIT_CHN_MASK (1LL << ADC_CONF_CHN_RANGE) | \
(1LL << ADC_CONF_CHN_TERMINATION) | \
(1LL << ADC_CONF_CHN_OFFSET) | \
(1LL << ADC_CONF_CHN_SATURATION)
#define ADC_100M_4CH_14BIT_BRD_MASK (1LL << ADC_CONF_BRD_STATE_MACHINE_STATUS) | \
(1LL << ADC_CONF_BRD_N_CHAN) | \
(1LL << ADC_CONF_BRD_N_TRG_EXT) | \
(1LL << ADC_CONF_BRD_N_TRG_THR) | \
(1LL << ADC_CONF_BRD_N_TRG_TIM)
#define ADC_100M_4CH_14BIT_CUS_MASK (1ULL << ADC_CONF_100M14B4CHA_BUF_TYPE) | \
(1ULL << ADC_CONF_100M14B4CHA_TRG_SW_EN) | \
(1ULL << ADC_CONF_100M14B4CHA_ACQ_MSHOT_MAX) | \
(1ULL << ADC_CONF_100M14B4CHA_BUF_SIZE_KB)
#define ADC_100M_4CH_14BIT_TRG_EXT_MASK (1LL << ADC_CONF_TRG_EXT_ENABLE) | \
(1LL << ADC_CONF_TRG_EXT_POLARITY) | \
(1LL << ADC_CONF_TRG_EXT_DELAY)
#define ADC_100M_4CH_14BIT_TRG_THR_MASK (1LL << ADC_CONF_TRG_THR_ENABLE) | \
(1LL << ADC_CONF_TRG_THR_POLARITY) | \
(1LL << ADC_CONF_TRG_THR_DELAY) | \
(1LL << ADC_CONF_TRG_THR_THRESHOLD) | \
(1LL << ADC_CONF_TRG_THR_HYSTERESIS)
#define ADC_100M_4CH_14BIT_TRG_TIM_MASK 0x0
static
typeof
(
adc_get_param
)
*
adc_param
[]
=
{
[
ADC_CONF_GET
]
=
adc_get_param
,
[
ADC_CONF_SET
]
=
adc_set_param
,
};
/**
* ADC internal data
*/
struct
adc_100m14b4cha
{
/**< keep track of all zero-offset values for each range
(100mV 1V 10V) */
int32_t
offset_zero
[
FA100M14B4C_NCHAN
][
ADC_CONF_100M14B4CHA_CHN_RANGE_N
];
};
static
int
adc_100m14b4cha_offset_zero_set
(
struct
adc_dev
*
dev
);
/**
* According to totalsamples and nbuffers it resize the buffer to feet
...
...
@@ -133,10 +176,35 @@ static struct adc_dev *adc_100m14b4cha_open(const struct adc_board_type *b,
/* try to size it, do not report errors */
adc_100m14b4cha_buffer_resize
(
dev
,
totalsamples
,
nbuffer
);
dev
=
(
void
*
)
&
fa
->
gid
;
fa
->
priv
=
malloc
(
sizeof
(
struct
adc_100m14b4cha
));
if
(
!
fa
->
priv
)
return
NULL
;
memset
(
fa
->
priv
,
0
,
sizeof
(
struct
adc_100m14b4cha
));
err
=
adc_100m14b4cha_offset_zero_set
(
dev
);
if
(
err
)
goto
err
;
out:
return
dev
;
err:
free
(
fa
->
priv
);
return
NULL
;
}
/**
* @copydoc adc_close
*/
static
int
adc_100m14b4cha_close
(
struct
adc_dev
*
dev
)
{
struct
__adc_dev_zio
*
fa
=
to_dev_zio
(
dev
);
free
(
fa
->
priv
);
return
adc_zio_close
(
dev
);
}
/**
* @copydoc adc_acq_start
...
...
@@ -319,6 +387,7 @@ static int adc_100m14b4cha_config_chn(struct adc_dev *adc,
{
struct
__adc_dev_zio
*
fa
=
to_dev_zio
(
adc
);
char
path
[
1024
];
int
err
;
if
(
source
>
ADC_100M14B4CHA_MAX_CHN_SRC
-
1
)
{
errno
=
ADC_EROUTE
;
...
...
@@ -335,6 +404,9 @@ static int adc_100m14b4cha_config_chn(struct adc_dev *adc,
case
ADC_CONF_CHN_OFFSET
:
sprintf
(
path
,
"cset%d/ch%d-offset"
,
fa
->
cset
,
source
);
break
;
case
__ADC_CONF_CHN_OFFSET_ZERO
:
sprintf
(
path
,
"cset%d/ch%d-offset-zero"
,
fa
->
cset
,
source
);
break
;
case
ADC_CONF_CHN_SATURATION
:
sprintf
(
path
,
"cset%d/ch%d-saturation"
,
fa
->
cset
,
source
);
break
;
...
...
@@ -342,7 +414,17 @@ static int adc_100m14b4cha_config_chn(struct adc_dev *adc,
errno
=
ADC_ENOCAP
;
return
-
1
;
}
return
adc_param
[
direction
](
adc
,
path
,
NULL
,
(
int
*
)
value
);
err
=
adc_param
[
direction
](
adc
,
path
,
NULL
,
(
int
*
)
value
);
if
(
!
err
&&
direction
==
ADC_CONF_SET
&&
index
==
ADC_CONF_CHN_RANGE
)
{
/*
* Whenever the user changes voltage range we need to adjust
* the zero-offset value (if any)
*/
return
adc_100m14b4cha_offset_zero_set
(
adc
);
}
return
err
;
}
...
...
@@ -719,38 +801,474 @@ static int adc_100m14b4cha_trigger_fire(struct adc_dev *dev)
NULL
,
&
value
);
}
#define ADC_100M_4CH_14BIT_ACQ_MASK (1LL << ADC_CONF_ACQ_N_SHOTS) | \
(1LL << ADC_CONF_ACQ_POST_SAMP) | \
(1LL << ADC_CONF_ACQ_PRE_SAMP) | \
(1LL << ADC_CONF_ACQ_UNDERSAMPLE) | \
(1LL << ADC_CONF_ACQ_FREQ_HZ) | \
(1LL << ADC_CONF_ACQ_N_BITS)
#define ADC_100M_4CH_14BIT_CHN_MASK (1LL << ADC_CONF_CHN_RANGE) | \
(1LL << ADC_CONF_CHN_TERMINATION) | \
(1LL << ADC_CONF_CHN_OFFSET) | \
(1LL << ADC_CONF_CHN_SATURATION)
#define ADC_100M_4CH_14BIT_BRD_MASK (1LL << ADC_CONF_BRD_STATE_MACHINE_STATUS) | \
(1LL << ADC_CONF_BRD_N_CHAN) | \
(1LL << ADC_CONF_BRD_N_TRG_EXT) | \
(1LL << ADC_CONF_BRD_N_TRG_THR) | \
(1LL << ADC_CONF_BRD_N_TRG_TIM)
#define ADC_100M_4CH_14BIT_CUS_MASK (1ULL << ADC_CONF_100M14B4CHA_BUF_TYPE) | \
(1ULL << ADC_CONF_100M14B4CHA_TRG_SW_EN) | \
(1ULL << ADC_CONF_100M14B4CHA_ACQ_MSHOT_MAX) | \
(1ULL << ADC_CONF_100M14B4CHA_BUF_SIZE_KB)
#define ADC_100M_4CH_14BIT_TRG_EXT_MASK (1LL << ADC_CONF_TRG_EXT_ENABLE) | \
(1LL << ADC_CONF_TRG_EXT_POLARITY) | \
(1LL << ADC_CONF_TRG_EXT_DELAY)
#define ADC_100M_4CH_14BIT_TRG_THR_MASK (1LL << ADC_CONF_TRG_THR_ENABLE) | \
(1LL << ADC_CONF_TRG_THR_POLARITY) | \
(1LL << ADC_CONF_TRG_THR_DELAY) | \
(1LL << ADC_CONF_TRG_THR_THRESHOLD) | \
(1LL << ADC_CONF_TRG_THR_HYSTERESIS)
#define ADC_100M_4CH_14BIT_TRG_TIM_MASK 0x0
static
int
adc_100m14b4cha_buffer_get_sample
(
struct
adc_buffer
*
buf
,
unsigned
int
chan
,
unsigned
int
acq_sample
,
int32_t
*
value
)
{
if
(
chan
>=
FA100M14B4C_NCHAN
)
{
errno
=
EINVAL
;
return
-
1
;
}
*
value
=
((
int16_t
*
)
buf
->
data
)[
acq_sample
*
FA100M14B4C_NCHAN
+
chan
];
return
0
;
}
struct
tmp_cfg_store
{
uint32_t
trg_source
;
struct
adc_conf
chn
[
FA100M14B4C_NCHAN
];
struct
adc_conf
acq
;
};
static
int
__cfg_offac_save
(
struct
adc_dev
*
dev
,
struct
tmp_cfg_store
*
cfg
,
unsigned
long
flags
)
{
int
err
,
i
;
if
((
flags
&
ADC_OFFSET_AC_F_MANUAL
)
||
!
(
flags
&
ADC_OFFSET_AC_F_RESTORE
))
return
0
;
cfg
->
acq
.
type
=
ADC_CONF_TYPE_ACQ
;
cfg
->
acq
.
mask
=
(
1ULL
<<
ADC_CONF_ACQ_N_SHOTS
)
|
(
1ULL
<<
ADC_CONF_ACQ_PRE_SAMP
)
|
(
1ULL
<<
ADC_CONF_ACQ_POST_SAMP
)
|
(
1ULL
<<
ADC_CONF_ACQ_UNDERSAMPLE
);
err
=
adc_retrieve_config
(
dev
,
&
cfg
->
acq
);
if
(
err
)
goto
err
;
for
(
i
=
0
;
i
<
FA100M14B4C_NCHAN
;
++
i
)
{
memset
(
&
cfg
->
chn
[
i
],
0
,
sizeof
(
struct
adc_conf
));
cfg
->
chn
[
i
].
type
=
ADC_CONF_TYPE_CHN
;
cfg
->
chn
[
i
].
mask
=
ADC_100M_4CH_14BIT_CHN_MASK
;
}
err
=
adc_retrieve_config_n
(
dev
,
cfg
->
chn
,
FA100M14B4C_NCHAN
);
if
(
err
)
goto
err
;
err
=
adc_get_param
(
dev
,
"cset0/trigger/source"
,
NULL
,
(
int
*
)
&
cfg
->
trg_source
);
if
(
err
)
goto
err
;
return
0
;
err:
errno
=
ADC_OFF_AC_RESTORE_S
;
return
err
;
}
static
int
__cfg_offac_restore
(
struct
adc_dev
*
dev
,
struct
tmp_cfg_store
*
cfg
,
unsigned
long
flags
)
{
int
err
=
0
;
if
((
flags
&
ADC_OFFSET_AC_F_MANUAL
)
||
!
(
flags
&
ADC_OFFSET_AC_F_RESTORE
))
return
0
;
err
|=
adc_apply_config
(
dev
,
0
,
&
cfg
->
acq
);
err
|=
adc_apply_config_n
(
dev
,
0
,
cfg
->
chn
,
FA100M14B4C_NCHAN
);
err
|=
adc_set_param
(
dev
,
"cset0/trigger/source"
,
NULL
,
(
int
*
)
&
cfg
->
trg_source
);
if
(
err
)
errno
=
ADC_OFF_AC_RESTORE_R
;
return
err
;
}
/*
* Following what the configuration for the offset clear computation
*/
#define CFG_OFFAC_NSAMPLES 100000
#define CFG_OFFAC_N 6
static
struct
adc_conf
cfg_offac
[
CFG_OFFAC_N
]
=
{
{
.
type
=
ADC_CONF_TYPE_ACQ
,
.
mask
=
(
1ULL
<<
ADC_CONF_ACQ_N_SHOTS
)
|
(
1ULL
<<
ADC_CONF_ACQ_PRE_SAMP
)
|
(
1ULL
<<
ADC_CONF_ACQ_POST_SAMP
)
|
(
1ULL
<<
ADC_CONF_ACQ_UNDERSAMPLE
),
.
value
=
{
[
ADC_CONF_ACQ_N_SHOTS
]
=
1
,
[
ADC_CONF_ACQ_PRE_SAMP
]
=
0
,
[
ADC_CONF_ACQ_POST_SAMP
]
=
CFG_OFFAC_NSAMPLES
,
[
ADC_CONF_ACQ_UNDERSAMPLE
]
=
0
,
},
},
{
.
type
=
ADC_CONF_TYPE_CUS
,
.
mask
=
(
1ULL
<<
ADC_CONF_100M14B4CHA_TRG_SW_EN
)
|
(
1ULL
<<
ADC_CONF_100M14B4CHA_BUF_SIZE_KB
),
.
value
=
{
[
ADC_CONF_100M14B4CHA_TRG_SW_EN
]
=
1
,
[
ADC_CONF_100M14B4CHA_BUF_SIZE_KB
]
=
2048
,
},
},
{
.
type
=
ADC_CONF_TYPE_CHN
,
.
route_to
=
0
,
.
mask
=
(
1LL
<<
ADC_CONF_CHN_TERMINATION
)
|
(
1LL
<<
ADC_CONF_CHN_OFFSET
)
|
(
1LL
<<
ADC_CONF_CHN_SATURATION
),
.
value
=
{
[
ADC_CONF_CHN_TERMINATION
]
=
0
,
[
ADC_CONF_CHN_OFFSET
]
=
0
,
[
ADC_CONF_CHN_SATURATION
]
=
0x7FFF
,
},
},
{
.
type
=
ADC_CONF_TYPE_CHN
,
.
route_to
=
1
,
.
mask
=
(
1LL
<<
ADC_CONF_CHN_TERMINATION
)
|
(
1LL
<<
ADC_CONF_CHN_OFFSET
)
|
(
1LL
<<
ADC_CONF_CHN_SATURATION
),
.
value
=
{
[
ADC_CONF_CHN_TERMINATION
]
=
0
,
[
ADC_CONF_CHN_OFFSET
]
=
0
,
[
ADC_CONF_CHN_SATURATION
]
=
0x7FFF
,
},
},
{
.
type
=
ADC_CONF_TYPE_CHN
,
.
route_to
=
2
,
.
mask
=
(
1LL
<<
ADC_CONF_CHN_TERMINATION
)
|
(
1LL
<<
ADC_CONF_CHN_OFFSET
)
|
(
1LL
<<
ADC_CONF_CHN_SATURATION
),
.
value
=
{
[
ADC_CONF_CHN_TERMINATION
]
=
0
,
[
ADC_CONF_CHN_OFFSET
]
=
0
,
[
ADC_CONF_CHN_SATURATION
]
=
0x7FFF
,
},
},
{
.
type
=
ADC_CONF_TYPE_CHN
,
.
route_to
=
3
,
.
mask
=
(
1LL
<<
ADC_CONF_CHN_TERMINATION
)
|
(
1LL
<<
ADC_CONF_CHN_OFFSET
)
|
(
1LL
<<
ADC_CONF_CHN_SATURATION
),
.
value
=
{
[
ADC_CONF_CHN_TERMINATION
]
=
0
,
[
ADC_CONF_CHN_OFFSET
]
=
0
,
[
ADC_CONF_CHN_SATURATION
]
=
0x7FFF
,
},
}
};
/**
* It configures the ADC for the offset auto-clear
* @param[in] dev ADC token
* @param[in] chan channel number [0, 4]
* @param[in] flags options
*
* @return 0 on success, otherwise -1 and errno is appropriately set
*
* pre-sample: 0
* post-sample: 100000
* undersample: 0
* nshots: 1
* offset: 0
* range: open-drain
* sw-trg: enable
* buf-size: 2MiB
*/
static
int
__cfg_offac_apply
(
struct
adc_dev
*
dev
,
unsigned
long
flags
)
{
int
err
,
trgsrc
=
FA100M14B4C_TRG_SRC_SW
,
i
;
uint32_t
offset
=
0
;
/* zero-offset needs to be cleard always */
for
(
i
=
0
;
i
<
FA100M14B4C_NCHAN
;
++
i
)
{
err
=
adc_100m14b4cha_config_chn
(
dev
,
i
,
__ADC_CONF_CHN_OFFSET_ZERO
,
&
offset
,
ADC_CONF_SET
);
if
(
err
)
return
err
;
}
if
(
flags
&
ADC_OFFSET_AC_F_MANUAL
)
return
0
;
err
=
adc_set_param
(
dev
,
"cset0/trigger/source"
,
NULL
,
&
trgsrc
);
if
(
err
)
return
err
;
err
=
adc_apply_config_n
(
dev
,
0
,
cfg_offac
,
CFG_OFFAC_N
);
if
(
err
)
return
err
;
return
0
;
}
/**
* It converts an hardware value to uV
* @param[in] val value to be converted (hardware format)
* @param[in] range switch configuration read from the hardware
*
* @return uV value
*/
static
int32_t
__convert_hw_to_uv
(
int32_t
val
,
unsigned
int
range
)
{
int32_t
result_int
,
result_frac
,
range_uV
,
factor
;
switch
(
range
)
{
case
ADC_CONF_100M14B4CHA_CHN_RANGE_10V_CAL
:
case
ADC_CONF_100M14B4CHA_CHN_RANGE_10V
:
/* 10V */
range_uV
=
10000000
;
break
;
case
ADC_CONF_100M14B4CHA_CHN_RANGE_100mV_CAL
:
case
ADC_CONF_100M14B4CHA_CHN_RANGE_100mV
:
/* 100mV */
range_uV
=
100000
;
break
;
case
ADC_CONF_100M14B4CHA_CHN_RANGE_1V_CAL
:
case
ADC_CONF_100M14B4CHA_CHN_RANGE_1V
:
case
ADC_CONF_100M14B4CHA_CHN_RANGE_OPEN_DRAIN
:
/* 1V */
range_uV
=
1000000
;
break
;
default:
fprintf
(
stderr
,
"Invalid switch configuration 0x%x
\n
"
,
range
);
return
0
;
}
factor
=
(
range_uV
/
BIT
(
14
));
result_int
=
((
val
>>
2
)
*
factor
);
result_frac
=
((
val
&
0x3
)
*
10
)
*
(
factor
/
1000
);
return
result_int
+
result_frac
;
}
static
int
__offz_rng_idx_get
(
enum
adc_configuration_100m14b4cha_channel_range
range
)
{
int
idx
=
0
;
switch
(
range
)
{
case
ADC_CONF_100M14B4CHA_CHN_RANGE_100mV
:
case
ADC_CONF_100M14B4CHA_CHN_RANGE_100mV_CAL
:
idx
=
0
;
break
;
case
ADC_CONF_100M14B4CHA_CHN_RANGE_1V
:
case
ADC_CONF_100M14B4CHA_CHN_RANGE_1V_CAL
:
case
ADC_CONF_100M14B4CHA_CHN_RANGE_OPEN_DRAIN
:
idx
=
1
;
break
;
case
ADC_CONF_100M14B4CHA_CHN_RANGE_10V
:
case
ADC_CONF_100M14B4CHA_CHN_RANGE_10V_CAL
:
idx
=
2
;
break
;
}
return
idx
;
}
/**
* According to the current voltage range, it sets the zero-offset
* value
*/
static
int
adc_100m14b4cha_offset_zero_set
(
struct
adc_dev
*
dev
)
{
struct
__adc_dev_zio
*
fa
=
to_dev_zio
(
dev
);
struct
adc_100m14b4cha
*
adcdata
=
fa
->
priv
;
int
i
,
err
=
0
;
for
(
i
=
0
;
i
<
FA100M14B4C_NCHAN
;
++
i
)
{
uint32_t
offset
,
range
;
int
ret
;
ret
=
adc_100m14b4cha_config_chn
(
dev
,
i
,
ADC_CONF_CHN_RANGE
,
&
range
,
ADC_CONF_GET
);
if
(
ret
)
{
err
|=
ret
;
continue
;
}
offset
=
adcdata
->
offset_zero
[
i
][
__offz_rng_idx_get
(
range
)];
err
|=
adc_100m14b4cha_config_chn
(
dev
,
i
,
__ADC_CONF_CHN_OFFSET_ZERO
,
&
offset
,
ADC_CONF_SET
);
}
return
err
?
-
1
:
0
;
}
/**
* It sets a voltage range on all channels
* @param[in] dev ADC token
* @param[in] range voltage range
* @return 0 on success
*/
static
int
adc_100m14b4cha_range_set_all
(
struct
adc_dev
*
dev
,
enum
adc_configuration_100m14b4cha_channel_range
range
)
{
int
i
,
err
;
for
(
i
=
0
;
i
<
FA100M14B4C_NCHAN
;
++
i
)
{
err
=
adc_100m14b4cha_config_chn
(
dev
,
i
,
ADC_CONF_CHN_RANGE
,
&
range
,
ADC_CONF_SET
);
if
(
err
)
return
err
;
}
return
0
;
}
/**
* @copydoc adc_offset_auto_clear
*
* MANUAL MODE (user configuration)
*/
static
int
adc_100m14b4cha_offac_sw_avg_man
(
struct
adc_dev
*
dev
,
unsigned
long
flags
)
{
struct
__adc_dev_zio
*
fa
=
to_dev_zio
(
dev
);
struct
adc_100m14b4cha
*
adcdata
=
fa
->
priv
;
int32_t
offset
[
FA100M14B4C_NCHAN
];
uint32_t
ranges
[
FA100M14B4C_NCHAN
];
int
err
,
i
;
memset
(
ranges
,
0
,
sizeof
(
ranges
));
for
(
i
=
0
;
i
<
FA100M14B4C_NCHAN
;
++
i
)
{
err
=
adc_100m14b4cha_config_chn
(
dev
,
i
,
ADC_CONF_CHN_RANGE
,
&
ranges
[
i
],
ADC_CONF_GET
);
if
(
err
)
return
err
;
}
err
=
__cfg_offac_apply
(
dev
,
flags
);
if
(
err
)
return
err
;
err
=
adc_offset_auto_clear_sw_avg
(
dev
,
flags
,
offset
);
if
(
err
)
return
err
;
/*
* Since the Hw does (Vin - Vdac), to compensate a
* positive offset we need to apply a positive value
* to the DAC. For this reason we do not need to revert
* that value: the offset sign measured it is fine
*/
for
(
i
=
0
;
i
<
FA100M14B4C_NCHAN
;
++
i
)
{
int
range
=
__offz_rng_idx_get
(
ranges
[
i
]);
offset
[
i
]
=
__convert_hw_to_uv
(
offset
[
i
],
ranges
[
i
]);
adcdata
->
offset_zero
[
i
][
range
]
=
offset
[
i
];
}
return
0
;
}
/**
* @copydoc adc_offset_auto_clear
*
* AUTOMATIC MODE
*/
static
int
adc_100m14b4cha_offac_sw_avg_auto
(
struct
adc_dev
*
dev
,
unsigned
long
flags
)
{
struct
tmp_cfg_store
tmpcfg
;
struct
__adc_dev_zio
*
fa
=
to_dev_zio
(
dev
);
struct
adc_100m14b4cha
*
adcdata
=
fa
->
priv
;
uint32_t
ranges
[
ADC_CONF_100M14B4CHA_CHN_RANGE_N
]
=
{
ADC_CONF_100M14B4CHA_CHN_RANGE_100mV_CAL
,
ADC_CONF_100M14B4CHA_CHN_RANGE_1V_CAL
,
ADC_CONF_100M14B4CHA_CHN_RANGE_10V_CAL
,
};
int
i
,
err
=
0
,
err_rst
;
err
=
__cfg_offac_save
(
dev
,
&
tmpcfg
,
flags
);
if
(
err
)
return
err
;
err
=
__cfg_offac_apply
(
dev
,
flags
);
if
(
err
)
goto
out
;
/* Loop over all ranges and do the zero-offset calibration */
for
(
i
=
0
;
i
<
ADC_CONF_100M14B4CHA_CHN_RANGE_N
;
++
i
)
{
unsigned
int
range
=
ranges
[
i
];
int32_t
offset
[
FA100M14B4C_NCHAN
];
int
k
;
err
=
adc_100m14b4cha_range_set_all
(
dev
,
range
);
if
(
err
)
goto
out
;
err
=
adc_offset_auto_clear_sw_avg
(
dev
,
flags
,
offset
);
if
(
err
)
return
err
;
/*
* Since the Hw does (Vin - Vdac), to compensate a
* positive offset we need to apply a positive value
* to the DAC. For this reason we do not need to revert
* that value: the offset sign measured it is fine
*/
for
(
k
=
0
;
k
<
FA100M14B4C_NCHAN
;
++
k
)
{
offset
[
k
]
=
__convert_hw_to_uv
(
offset
[
k
],
range
);
adcdata
->
offset_zero
[
k
][
i
]
=
offset
[
k
];
}
}
out:
err_rst
=
__cfg_offac_restore
(
dev
,
&
tmpcfg
,
flags
);
if
(
err_rst
)
{
errno
=
ADC_OFF_AC_RESTORE_R
;
return
err_rst
;
}
return
err
;
}
/**
* @copydoc adc_offset_auto_clear
*/
static
int
adc_100m14b4cha_offset_auto_clear
(
struct
adc_dev
*
dev
,
unsigned
long
flags
)
{
struct
__adc_dev_zio
*
fa
=
to_dev_zio
(
dev
);
struct
adc_100m14b4cha
*
adcdata
=
fa
->
priv
;
int
err
;
/* this board does not have a hardware mechanism */
flags
|=
ADC_OFFSET_AC_F_SOFTWARE
;
if
(
flags
&
ADC_OFFSET_AC_F_ZERO
)
{
memset
(
adcdata
->
offset_zero
,
0
,
sizeof
(
int32_t
)
*
ADC_CONF_100M14B4CHA_CHN_RANGE_N
*
FA100M14B4C_NCHAN
);
goto
out
;
}
if
(
flags
&
ADC_OFFSET_AC_F_MANUAL
)
err
=
adc_100m14b4cha_offac_sw_avg_man
(
dev
,
flags
);
else
err
=
adc_100m14b4cha_offac_sw_avg_auto
(
dev
,
flags
);
if
(
err
)
return
err
;
out:
return
adc_100m14b4cha_offset_zero_set
(
dev
);
}
static
struct
adc_operations
fa_100ms_4ch_14bit_op
=
{
.
open
=
adc_100m14b4cha_open
,
.
close
=
adc_
zio
_close
,
.
close
=
adc_
100m14b4cha
_close
,
.
acq_start
=
adc_100m14b4cha_acq_start
,
.
acq_poll
=
adc_zio_acq_poll
,
...
...
@@ -767,6 +1285,10 @@ static struct adc_operations fa_100ms_4ch_14bit_op = {
.
tstamp_buffer
=
adc_zio_tstamp_buffer
,
.
release_buffer
=
adc_zio_release_buffer
,
.
trigger_fire
=
adc_100m14b4cha_trigger_fire
,
.
buffer_get_sample
=
adc_100m14b4cha_buffer_get_sample
,
.
buffer_fixup
=
NULL
,
.
offset_auto_clear
=
adc_100m14b4cha_offset_auto_clear
,
};
struct
adc_board_type
fmcadc_100ms_4ch_14bit
=
{
...
...
lib/lib-math.c
0 → 100644
View file @
c107af64
/*
* Routing public functions to device-specific code
*
* Copyright (C) 2018 CERN (www.cern.ch)
* Author: Federico Vaga <federico.vaga@cern.ch>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2 as published by the Free Software Foundation or, at your
* option, any later version.
*/
#include <errno.h>
#include <string.h>
#include "adc-lib.h"
#include "adc-lib-int.h"
/**
* It computes the avarege voltage within the given buffer
* @param[in] buf data set to use
* @param[in] chan channel number [0, NCHAN]
* @param[out] avg the computer avarage. The scale depends on the
* configuration,
* @return 0 on success, -1 on error and errno is set appropriately
* EINVAL: if the buffer is invalid, or channel is invalid
*/
int
adc_buffer_math_avg
(
struct
adc_buffer
*
buf
,
unsigned
int
chan
,
int32_t
*
avg
)
{
struct
adc_conf
cfg_brd
;
uint32_t
nchan
=
0
;
int
i
,
err
;
int64_t
total
=
0
;
memset
(
&
cfg_brd
,
0
,
sizeof
(
struct
adc_conf
));
cfg_brd
.
type
=
ADC_CONF_TYPE_BRD
;
adc_set_conf_mask_all
(
&
cfg_brd
,
buf
->
dev
);
err
=
adc_retrieve_config
(
buf
->
dev
,
&
cfg_brd
);
if
(
err
)
return
err
;
adc_get_conf
(
&
cfg_brd
,
ADC_CONF_BRD_N_CHAN
,
&
nchan
);
if
(
chan
>=
nchan
)
{
errno
=
EINVAL
;
return
-
1
;
}
for
(
i
=
0
;
i
<
buf
->
nsamples
;
++
i
)
{
int32_t
val
;
err
=
adc_buffer_get_sample
(
buf
,
chan
,
i
,
&
val
);
if
(
err
)
return
err
;
total
+=
val
;
}
*
avg
=
total
/
buf
->
nsamples
;
return
0
;
}
lib/lib.c
View file @
c107af64
...
...
@@ -9,6 +9,7 @@
* version 2 as published by the Free Software Foundation or, at your
* option, any later version.
*/
#include <errno.h>
#include <string.h>
#include "adc-lib.h"
#include "adc-lib-int.h"
...
...
@@ -31,6 +32,12 @@ static struct adc_errors {
{
ADC_ENOMASK
,
"Missing configuration mask"
},
{
ADC_EDISABLED
,
"Trigger is disabled: I/O aborted"
},
{
ADC_EROUTE
,
"Cannot route correctly the configuration"
},
{
ADC_ENOP_SWTRG
,
"Operation not supported: software trigger"
},
{
ADC_ENOP_OFFCLR
,
"Operation not supported: offset auto-clear"
},
{
ADC_ENOP_OFFCLRHW
,
"Operation not supported: offset auto-clear hardware"
},
{
ADC_ENOP_OFFCLRSW
,
"Operation not supported: offset auto-clear software"
},
{
ADC_OFF_AC_RESTORE_S
,
"Offset auto-clear: cannot store configuration"
},
{
ADC_OFF_AC_RESTORE_R
,
"Offset auto-clear: cannot restore configuration"
},
{
0
,
}
};
...
...
@@ -86,3 +93,96 @@ uint64_t adc_get_capabilities(struct adc_dev *dev,
return
b
->
board
->
capabilities
[
type
];
}
/**
* It computes what are the necessary offsets to apply on channels
* in order to clear a constant offset.
* @param[in] dev ADC device token
* @param[in] flags options @see adc_offset_auto_clear_flags
* @param[out] offset vector of compensation offsets (one for each channel)
* @return 0 on success, -1 on error and errno is set appropriately
* EINVAL: invalid flags value
* ADC_ENOP_SWTRG: when software trigger is missing
*
* The configuration is board dependent, so here we just run and apply
* the compensation offset. The configuration is left to the user.
*
* Since this function uses software trigger, the user should disable
* all trigger sources except the software one.
*
* NOTE: offset is a vector, so when you call this function be careful
* about the size of that parameter, it must big enough to host a value
* for each board channel.
*/
int
adc_offset_auto_clear_sw_avg
(
struct
adc_dev
*
dev
,
unsigned
long
flags
,
int32_t
*
offset
)
{
struct
adc_conf
cfg
;
struct
adc_buffer
*
buf
;
struct
timeval
tv
=
{
0
,
0
};
uint32_t
nchan
,
pre
,
post
;
int
err
,
err_stop
,
i
;
unsigned
int
nsamples
;
if
(
!
(
flags
&
ADC_OFFSET_AC_F_SOFTWARE
))
{
errno
=
EINVAL
;
return
-
1
;
}
if
(
!
adc_has_trigger_fire
(
dev
))
{
errno
=
ADC_ENOP_SWTRG
;
return
-
1
;
}
memset
(
&
cfg
,
0
,
sizeof
(
struct
adc_conf
));
cfg
.
type
=
ADC_CONF_TYPE_ACQ
;
adc_set_conf_mask
(
&
cfg
,
ADC_CONF_ACQ_PRE_SAMP
);
adc_set_conf_mask
(
&
cfg
,
ADC_CONF_ACQ_POST_SAMP
);
err
=
adc_retrieve_config
(
dev
,
&
cfg
);
if
(
err
)
return
err
;
adc_get_conf
(
&
cfg
,
ADC_CONF_ACQ_PRE_SAMP
,
&
pre
);
adc_get_conf
(
&
cfg
,
ADC_CONF_ACQ_POST_SAMP
,
&
post
);
nsamples
=
pre
+
post
;
memset
(
&
cfg
,
0
,
sizeof
(
struct
adc_conf
));
cfg
.
type
=
ADC_CONF_TYPE_BRD
;
adc_set_conf_mask
(
&
cfg
,
ADC_CONF_BRD_N_CHAN
);
err
=
adc_retrieve_config
(
dev
,
&
cfg
);
if
(
err
)
return
err
;
adc_get_conf
(
&
cfg
,
ADC_CONF_BRD_N_CHAN
,
&
nchan
);
buf
=
adc_request_buffer
(
dev
,
nsamples
,
NULL
,
0
);
if
(
!
buf
)
return
-
1
;
err
=
adc_acq_start
(
dev
,
ADC_F_FLUSH
,
&
tv
);
if
(
err
)
goto
out
;
err
=
adc_trigger_fire
(
dev
);
if
(
err
)
goto
out
;
tv
.
tv_sec
=
10
;
err
=
adc_fill_buffer
(
dev
,
buf
,
0
,
&
tv
);
if
(
err
)
goto
out
;
for
(
i
=
0
;
i
<
nchan
;
++
i
)
{
err
=
adc_buffer_math_avg
(
buf
,
i
,
&
offset
[
i
]);
if
(
err
)
goto
out
;
}
out:
adc_release_buffer
(
dev
,
buf
,
NULL
);
err_stop
=
adc_acq_stop
(
dev
,
0
);
if
(
err_stop
)
return
err_stop
;
return
err
;
}
lib/route.c
View file @
c107af64
...
...
@@ -75,6 +75,8 @@ static const struct adc_board_type *find_board(char *name)
* It should be passed around but not be looked into.
*
* @todo Open should choose the buffer type
*
* It initialize the board, this can be different board to board.
*/
struct
adc_dev
*
adc_open
(
char
*
name
,
unsigned
int
dev_id
,
unsigned
long
totalsamples
,
...
...
@@ -254,13 +256,19 @@ int adc_acq_stop(struct adc_dev *dev, unsigned int flags)
* @param[in] flags
* @param[in] conf configuration to apply
* @return 0 on success, -1 on error and errno is set appropriately
*
* If the configuration cannot be applied entirely, then this function
* will set ADC_CONF_F_ERROR in conf->flags
*/
int
adc_apply_config
(
struct
adc_dev
*
dev
,
unsigned
int
flags
,
struct
adc_conf
*
conf
)
struct
adc_conf
*
conf
)
{
struct
adc_gid
*
g
=
(
struct
adc_gid
*
)
dev
;
const
struct
adc_board_type
*
b
=
g
->
board
;
uint64_t
cap_mask
;
int
err
;
conf
->
flags
&=
~
ADC_CONF_F_ERROR
;
if
(
!
b
->
adc_op
->
apply_config
)
{
errno
=
ADC_ENOP
;
...
...
@@ -282,9 +290,40 @@ int adc_apply_config(struct adc_dev *dev, unsigned int flags,
errno
=
ADC_ENOCAP
;
return
-
1
;
}
return
b
->
adc_op
->
apply_config
(
dev
,
flags
,
conf
);
err
=
b
->
adc_op
->
apply_config
(
dev
,
flags
,
conf
);
if
(
err
)
conf
->
flags
|=
ADC_CONF_F_ERROR
;
return
err
;
}
/**
* It applies the given configuration to the device
* @param[in] dev ADC device token
* @param[in] flags
* @param[in] conf list of configurations to apply
* @param[in] n number of valid configurations in conf
* @return 0 on success, -1 on error and errno is set appropriately
*
* If a configuration cannot be applied entirely, then this function
* will set ADC_CONF_F_ERROR in conf[i].flags
*/
int
adc_apply_config_n
(
struct
adc_dev
*
dev
,
unsigned
int
flags
,
struct
adc_conf
*
conf
,
unsigned
int
n
)
{
unsigned
int
i
;
int
err_last
=
0
;
for
(
i
=
0
;
i
<
n
;
++
i
)
{
int
err
=
adc_apply_config
(
dev
,
flags
,
&
conf
[
i
]);
if
(
err
)
{
err_last
=
err
;
continue
;
}
}
return
err_last
;
}
/**
* It retrieve the current device configuration
...
...
@@ -292,12 +331,18 @@ int adc_apply_config(struct adc_dev *dev, unsigned int flags,
* @param[in,out] conf configuration descriptor to fill with configuration
* values.
* @return 0 on success, -1 on error and errno is set appropriately
*
* If the configuration cannot be retrieved entirely, then this function
* will set ADC_CONF_F_ERROR in conf->flags
*/
int
adc_retrieve_config
(
struct
adc_dev
*
dev
,
struct
adc_conf
*
conf
)
{
struct
adc_gid
*
g
=
(
struct
adc_gid
*
)
dev
;
const
struct
adc_board_type
*
b
=
g
->
board
;
uint64_t
cap_mask
;
int
err
;
conf
->
flags
&=
~
ADC_CONF_F_ERROR
;
if
(
!
b
->
adc_op
->
retrieve_config
)
{
errno
=
ADC_ENOP
;
...
...
@@ -319,9 +364,39 @@ int adc_retrieve_config(struct adc_dev *dev, struct adc_conf *conf)
errno
=
ADC_ENOCAP
;
return
-
1
;
}
return
b
->
adc_op
->
retrieve_config
(
dev
,
conf
);
err
=
b
->
adc_op
->
retrieve_config
(
dev
,
conf
);
if
(
err
)
conf
->
flags
|=
ADC_CONF_F_ERROR
;
return
err
;
}
/**
* It retrieve the current device configuration
* @param[in] dev ADC device token
* @param[in,out] conf configuration descriptor to fill with configuration
* values.
* @param[in] n number of valid configurations in conf
* @return 0 on success, -1 on error and errno is set appropriately
*
* If a configuration cannot be retrieved entirely, then this function
* will set ADC_CONF_F_ERROR in conf[i].flags
*/
int
adc_retrieve_config_n
(
struct
adc_dev
*
dev
,
struct
adc_conf
*
conf
,
unsigned
int
n
)
{
unsigned
int
i
;
int
err_last
=
0
;
for
(
i
=
0
;
i
<
n
;
++
i
)
{
int
err
=
adc_retrieve_config
(
dev
,
&
conf
[
i
]);
if
(
err
)
{
err_last
=
err
;
continue
;
}
}
return
err_last
;
}
/**
* It get a single parameter from the device
...
...
@@ -402,6 +477,7 @@ struct adc_buffer *adc_request_buffer(struct adc_dev *dev,
* @param[in] dev ADC device token
* @param[in] buf the buffer to fill
* @param[in] flags used to control how to fill the buffer
* (ADC_F_FIXUP)
* @param[in] timeout maximum time to fill the buffer. The behavior
* is similar to *select(1)*. If NULL, there is no timeout and
* the function will wait until the data is ready.
...
...
@@ -419,13 +495,21 @@ int adc_fill_buffer(struct adc_dev *dev,
{
struct
adc_gid
*
g
=
(
struct
adc_gid
*
)
dev
;
const
struct
adc_board_type
*
b
=
g
->
board
;
int
err
;
if
(
!
b
->
adc_op
->
fill_buffer
)
{
errno
=
ADC_ENOP
;
return
-
1
;
}
return
b
->
adc_op
->
fill_buffer
(
dev
,
buf
,
flags
,
timeout
);
err
=
b
->
adc_op
->
fill_buffer
(
dev
,
buf
,
flags
,
timeout
);
if
(
err
)
return
err
;
if
(
flags
&
ADC_F_FIXUP
)
return
adc_buffer_fixup
(
buf
);
return
0
;
}
...
...
@@ -499,6 +583,7 @@ int adc_has_trigger_fire(struct adc_dev *dev)
* It forces the board to trigger the acquisition
* @param[in] dev ADC device token
* @return 0 on success, -1 on error and errno is set appropriately
* ADC_ENOP_SWTRG: when software trigger is not supported
*/
int
adc_trigger_fire
(
struct
adc_dev
*
dev
)
{
...
...
@@ -506,8 +591,101 @@ int adc_trigger_fire(struct adc_dev *dev)
const
struct
adc_board_type
*
b
=
g
->
board
;
if
(
!
b
->
adc_op
->
trigger_fire
)
{
errno
=
ADC_ENOP
;
errno
=
ADC_ENOP
_SWTRG
;
return
-
1
;
}
return
b
->
adc_op
->
trigger_fire
(
dev
);
}
/**
* It gets a sample from the buffer
* @param[in] buf buffer to use
* @param[in] chan which channel
* @param[in] acq_sample acquisition sample
* @param[out] value
* @return 0 on success, -1 on error and errno is set appropriately
* ADC_ENOP if the operation is not supported by the board that
* acquired the buffer
*/
int
adc_buffer_get_sample
(
struct
adc_buffer
*
buf
,
unsigned
int
chan
,
unsigned
int
acq_sample
,
int32_t
*
value
)
{
struct
adc_gid
*
g
=
(
struct
adc_gid
*
)
buf
->
dev
;
const
struct
adc_board_type
*
b
=
g
->
board
;
if
(
!
b
->
adc_op
->
buffer_get_sample
)
{
errno
=
ADC_ENOP
;
return
-
1
;
}
if
(
acq_sample
>=
buf
->
nsamples
)
{
errno
=
EINVAL
;
return
-
1
;
}
return
b
->
adc_op
->
buffer_get_sample
(
buf
,
chan
,
acq_sample
,
value
);
}
/**
* It fixes the given buffer if there is any need
* @param[in, out] buf acquistion buffer (filled)
* @return 0 on success, -1 on error and errno is set appropriately
*
* This is used by board specifc code to compensate some known error
*/
int
adc_buffer_fixup
(
struct
adc_buffer
*
buf
)
{
struct
adc_gid
*
g
=
(
struct
adc_gid
*
)
buf
->
dev
;
const
struct
adc_board_type
*
b
=
g
->
board
;
if
(
b
->
adc_op
->
buffer_fixup
)
return
b
->
adc_op
->
buffer_fixup
(
buf
);
return
0
;
}
/**
* It checks if the board support offset auto-clear
* @param[in] dev ADC device token
* @return 1 when it does support offset auto clear; 0 when it does not
*/
int
adc_has_offset_auto_clear
(
struct
adc_dev
*
dev
)
{
struct
adc_gid
*
g
=
(
struct
adc_gid
*
)
dev
;
const
struct
adc_board_type
*
b
=
g
->
board
;
return
!!
b
->
adc_op
->
offset_auto_clear
;
}
/**
* It clears eventual offsets on all channel
* @param[in] dev ADC device token
* @param[in] flags options @see adc_offset_auto_clear_flags
* @return 0 on success, -1 on error and errno is set appropriately
* EINVAL: invalid flags value
* ADC_ENOP_OFFCLR: when offset auto-clear is not supported
*
* NOTE: The function may overwrite your current configuration (unless
* you use a flag) and it may remove any trace of previous acquisitions.
*
* Offset configuration is always overwritten (no matter what flag you use)
*/
int
adc_offset_auto_clear
(
struct
adc_dev
*
dev
,
unsigned
long
flags
)
{
struct
adc_gid
*
g
=
(
struct
adc_gid
*
)
dev
;
const
struct
adc_board_type
*
b
=
g
->
board
;
if
(
!
adc_has_offset_auto_clear
(
dev
))
{
errno
=
ADC_ENOP_OFFCLR
;
return
-
1
;
}
if
(
flags
&
~
__ADC_OFFSET_AC_F_MASK
)
{
errno
=
EINVAL
;
return
-
1
;
}
return
b
->
adc_op
->
offset_auto_clear
(
dev
,
flags
);
}
tools/adc-acq.c
View file @
c107af64
...
...
@@ -22,46 +22,95 @@
#include <unistd.h>
#include <inttypes.h>
#include <adc-lib.h>
#include <adc-lib-100m14b4cha.h>
static
int
arg_show_config
=
0
;
static
int
arg_no_read
=
0
;
static
int
arg_plot
=
0
;
static
int
arg_x_display
=
0
;
static
int
arg_trgsw_delay
=
0
;
static
int
arg_trgsw
=
0
;
static
int
fixup
=
0
;
static
int
statistics
=
0
;
static
char
git_version
[]
=
"version: "
GIT_VERSION
;
static
const
char
*
help_msg
=
"adc-acq -D <device-name>@0x<device-id> [OPTIONS]
\n\n
"
;
static
const
char
*
help_msg_opt
=
" --device|-D <device-name>@<device-id>: unique device identifier (e.g.:
\"
fmc-adc@0x0400
\"
)
\n
"
" --acquisition|-a <parameters> configure the acquisition
\n
"
" <pre-sample>,<post-sample>,<n-shots>,<undersample>
\n
"
" --trg-ext <parameters> configure an external trigger
\n
"
" <idx>,<enable>,<polarity>,<delay>
\n
"
" --trg-thr <parameters> configure a threshold trigger
\n
"
" <idx>,<enable>,<polarity>,<threshold>,<hysteresis>,<delay>
\n
"
" --trg-tim <parameters> configure a timing trigger
\n
"
" <NOT IMPLEMENTED YET>
\n
"
" --trg-sw <parameters> configure a software trigger
\n
"
" <delay_seconds>
\n
"
" --off-clr <ac-mode>,<idx> run offset-clear on a given channel
\n
"
" --channel| -c <parameters> configure an acquisition channel
\n
"
" <channel>,<termination>,<range>,<offset>,<saturation>
\n
"
" --timeout|-T <millisec> timeout for acquisition
\n
"
" --binary|-B <file> save binary to <file>
\n
"
" --multi-binary|-M <file> save two files per shot: "
"<file>.0000.ctrl etc
\n
"
" --dont-read|-N config-only
\n
"
" --loop|-l <num> number of loop before exiting
\n
"
" --show-data|-s <num> how many data to display: "
">0 from head, <0 from tail
\n
"
" --graph|-g <idx> plot the desired channel
\n
"
" --X11|-X Gnuplot will use X connection
\n
"
" --stats It prints some statistics
\n
"
" --version|-V print version information
\n
"
" --help|-h show this help
\n
"
"
\n
"
;
static
const
char
*
help_msg_opt_desc
=
"Parameter descriptions (alphabetic order)
\n
"
" <ac-mode>
\n
"
" Offset auto-clear mode mode, a: automatic (use library configuration), m: manual (use user configuration)
\n
"
" <enable>
\n
"
" enabled: 1, disabled: 0
\n
"
" <idx>
\n
"
" resource index [0, N]
\n
"
" <n-shots>
\n
"
" number of consecutive acquisitions
\n
"
" <offset>*
\n
"
" offset to apply on input channel
\n
"
" <polarity>
\n
"
" positive edge/slope: 0, negative edge/slope: 1
\n
"
" <post-sample>
\n
"
" number of samples to be acquired after trigger (including the trigger)
\n
"
" <pre-sample>
\n
"
" number of samples to be acquired before trigger
\n
"
" <range>*
\n
"
" voltage range to use
\n
"
" <saturation>*
\n
"
" saturation value for an input channel
\n
"
" <termination>
\n
"
" enabled: 1, disabled: 0
\n
"
" <threshold>*
\n
"
" <adc-value> that trigger the acquisition
\n
"
" <undersample>
\n
"
" number of samples to skip during the acquisition
\n
"
"
\n
"
" * The meaning depends on the board in used
\n
"
"
\n
"
;
static
void
fald_help
()
{
int
i
;
printf
(
"
\n
fald-acq [OPTIONS] -D <device-name>@0x<device-id>
\n\n
"
);
printf
(
" <device-name>: name of the device type to open
\n
"
);
fputs
(
help_msg
,
stderr
);
fprintf
(
stderr
,
" <device-name>: name of the device type to open
\n
"
);
for
(
i
=
0
;
i
<
__ADC_SUPPORTED_BOARDS_LAST_INDEX
;
i
++
)
printf
(
" %s
\n
"
,
adc_board_name
[
i
]);
printf
(
" --device|-D <device-name>@<device-id>: unique device identifier (e.g.:
\"
fmc-adc@0x0400
\"
)
\n
"
);
printf
(
" --acquisition|-a <parameters> configure the acquisition
\n
"
);
printf
(
" <pre-sample>,<post-sample>,<n-shots>,<undersample>
\n
"
);
printf
(
" --trg-ext <parameters> configure an external trigger
\n
"
);
printf
(
" <idx>,<enable>,<polarity>,<delay>
\n
"
);
printf
(
" --trg-thr <parameters> configure a threshold trigger
\n
"
);
printf
(
" <idx>,<enable>,<polarity>,<threshold>,<hysteresis>,<delay>
\n
"
);
printf
(
" --trg-tim <parameters> configure a timing trigger
\n
"
);
printf
(
" <NOT IMPLEMENTED YET>
\n
"
);
printf
(
" --channel| -c <parameters> configure an acquisition channel
\n
"
);
printf
(
" <channel>,<termination>,<range>,<offset>,<saturation>
\n
"
);
printf
(
" --timeout|-T <millisec> timeout for acquisition
\n
"
);
printf
(
" --binary|-B <file> save binary to <file>
\n
"
);
printf
(
" --multi-binary|-M <file> save two files per shot: "
"<file>.0000.ctrl etc
\n
"
);
printf
(
" --dont-read|-N config-only
\n
"
);
printf
(
" --loop|-l <num> number of loop before exiting
\n
"
);
printf
(
" --show-data|-s <num> how many data to display: "
">0 from head, <0 from tail
\n
"
);
printf
(
" --graph|-g <chnum> plot the desired channel
\n
"
);
printf
(
" --X11|-X Gnuplot will use X connection
\n
"
);
printf
(
" --version|-V print version information
\n
"
);
printf
(
" --help|-h show this help
\n\n
"
);
fprintf
(
stderr
,
" %s
\n
"
,
adc_board_name
[
i
]);
fputs
(
help_msg_opt
,
stderr
);
fputs
(
help_msg_opt_desc
,
stderr
);
}
...
...
@@ -69,6 +118,8 @@ enum fald_acq_options {
FALD_ACQ_OPT_TRG_EXT
=
1024
,
FALD_ACQ_OPT_TRG_THR
,
FALD_ACQ_OPT_TRG_TIM
,
FALD_ACQ_OPT_TRG_SW
,
FALD_ACQ_OPT_OFF_CLR
,
};
static
struct
option
options
[]
=
{
...
...
@@ -77,8 +128,13 @@ static struct option options[] = {
{
"trg-ext"
,
required_argument
,
0
,
FALD_ACQ_OPT_TRG_EXT
},
{
"trg-thr"
,
required_argument
,
0
,
FALD_ACQ_OPT_TRG_THR
},
{
"trg-tim"
,
required_argument
,
0
,
FALD_ACQ_OPT_TRG_TIM
},
{
"trg-sw"
,
required_argument
,
0
,
FALD_ACQ_OPT_TRG_SW
},
{
"channel"
,
required_argument
,
0
,
'c'
},
{
"timeout"
,
required_argument
,
0
,
'T'
},
{
"off-clr"
,
required_argument
,
0
,
FALD_ACQ_OPT_OFF_CLR
},
{
"fixup"
,
no_argument
,
&
fixup
,
1
},
{
"stats"
,
no_argument
,
&
statistics
,
1
},
/* new options, to help stress-test */
{
"binary"
,
required_argument
,
0
,
'B'
},
...
...
@@ -114,9 +170,7 @@ static int binmode;
static
int
timeout
=
-
1
;
static
int
loop
=
1
;
static
char
*
basefile
;
static
unsigned
int
nsamples
=
0
;
static
char
*
_argv
[
16
];
static
uint32_t
nchan
;
/* default is 1 V*/
static
double
bit_scale
=
0
.
5
/
(
1
<<
15
);
...
...
@@ -440,15 +494,15 @@ static int fald_acq_channel_configuration(struct adc_dev *adc, char *param)
*/
switch
(
range
)
{
case
100
:
range
=
0x23
;
range
=
ADC_CONF_100M14B4CHA_CHN_RANGE_100mV
;
bit_scale
=
0
.
05
/
(
1
<<
15
);
break
;
case
1
:
range
=
0x11
;
range
=
ADC_CONF_100M14B4CHA_CHN_RANGE_1V
;
bit_scale
=
0
.
5
/
(
1
<<
15
);
break
;
case
10
:
range
=
0x45
;
range
=
ADC_CONF_100M14B4CHA_CHN_RANGE_10V
;
bit_scale
=
5
.
0
/
(
1
<<
15
);
break
;
}
...
...
@@ -464,6 +518,46 @@ static int fald_acq_channel_configuration(struct adc_dev *adc, char *param)
return
adc_apply_config
(
adc
,
0
,
&
cfg
);
}
/**
* It configures a software trigger according to the parameters' string
* @param[in] adc The ADC device token
* @param[in] param a parameter string in the form
* "<delay>"
* @return 0 on success, otherwise -1 and errno is appropriately set
*
* The parameters are not mandatory. If not provided, the application will
* not try to set them (there is not a default value)
*/
static
int
fald_trg_software_configuration
(
struct
adc_dev
*
adc
,
char
*
param
)
{
uint32_t
delay
;
int
ret
;
ret
=
sscanf
(
param
,
"%"
SCNu32
,
&
delay
);
if
(
ret
<=
0
)
{
/* channel is mandatory */
errno
=
EINVAL
;
return
-
1
;
}
if
(
!
adc_has_trigger_fire
(
adc
))
{
fprintf
(
stderr
,
"Software trigger not supported
\n
"
);
errno
=
ADC_ENOP
;
return
-
1
;
}
arg_trgsw
=
1
;
switch
(
ret
)
{
case
1
:
arg_trgsw_delay
=
delay
;
break
;
default:
errno
=
EINVAL
;
return
-
1
;
}
return
0
;
}
/**
* It configures a external trigger according to the parameters' string
...
...
@@ -578,6 +672,59 @@ static int fald_trg_timer_configuration(struct adc_dev *adc, char *param)
return
adc_apply_config
(
adc
,
0
,
&
cfg
);
}
/**
* It peforms the auto-clear offset according to the command line options
* @param[in] adc The ADC device token
* @param[in] argc number of arguments
* @param[in] argv arguments
* @return 0 on success, otherwise -1 and errno is appropriately set
*/
static
int
adc_acq_offset_auto_clear
(
struct
adc_dev
*
adc
,
int
argc
,
char
*
argv
[])
{
char
offclr_mode
;
unsigned
long
flags
;
int
c
,
opt_index
,
ret
;
optind
=
1
;
/* set to 1 to make getopt_long happy */
/* Parse options */
while
((
c
=
getopt_long
(
argc
,
argv
,
GETOPT_STRING
,
options
,
&
opt_index
))
>=
0
)
{
switch
(
c
)
{
case
FALD_ACQ_OPT_OFF_CLR
:
ret
=
sscanf
(
optarg
,
"%c"
,
&
offclr_mode
);
if
(
ret
<=
0
)
{
errno
=
EINVAL
;
return
-
1
;
}
flags
=
0
;
switch
(
offclr_mode
)
{
case
'm'
:
flags
|=
ADC_OFFSET_AC_F_MANUAL
;
break
;
case
'a'
:
flags
|=
ADC_OFFSET_AC_F_RESTORE
;
break
;
case
'z'
:
flags
|=
ADC_OFFSET_AC_F_ZERO
;
break
;
default:
fprintf
(
stderr
,
"%s: invalid offset auto-clear mode '%c' (valid modes: {a: automatic, m: manual})"
,
_argv
[
0
],
offclr_mode
);
return
-
1
;
}
ret
=
adc_offset_auto_clear
(
adc
,
flags
);
if
(
ret
<
0
)
return
ret
;
break
;
default:
break
;
}
}
return
0
;
}
/**
* It parses command line arguments and consequential it configures the device
...
...
@@ -634,6 +781,14 @@ static int fald_acq_parse_args_and_configure(struct adc_dev *adc, int argc, char
return
-
1
;
}
break
;
case
FALD_ACQ_OPT_TRG_SW
:
err
=
fald_trg_software_configuration
(
adc
,
optarg
);
if
(
err
)
{
fprintf
(
stderr
,
"%s: Cannot configure software trigger: %s
\n
"
,
_argv
[
0
],
adc_strerror
(
errno
));
return
-
1
;
}
break
;
case
'T'
:
timeout
=
atoi
(
optarg
);
break
;
...
...
@@ -656,6 +811,10 @@ static int fald_acq_parse_args_and_configure(struct adc_dev *adc, int argc, char
}
}
err
=
adc_acq_offset_auto_clear
(
adc
,
argc
,
argv
);
if
(
err
)
return
-
1
;
fald_acq_print_config
(
adc
);
return
0
;
...
...
@@ -669,35 +828,31 @@ static int fald_acq_parse_args_and_configure(struct adc_dev *adc, int argc, char
* @param[in] acq_cfg acquisition configuration associated to the buffer
*/
static
void
fald_acq_print_data
(
struct
adc_buffer
*
buf
,
struct
adc_conf
*
acq_brd
,
struct
adc_conf
*
acq_cfg
,
unsigned
int
n
)
{
int
j
,
ch
;
int16_t
*
data
;
/* FMC-ADC-100M sample size is 14bit, 16bit for ZIO */
uint32_t
pre
;
uint32_t
pre
,
nchan
;
if
(
n
==
0
)
return
;
if
(
nsamples
!=
buf
->
nsamples
)
{
fprintf
(
stdout
,
"discrepancy between nsamples: %d and buf->nsamples: %d
\n
"
,
nsamples
,
buf
->
nsamples
);
return
;
}
adc_get_conf
(
acq_brd
,
ADC_CONF_BRD_N_CHAN
,
&
nchan
);
adc_get_conf
(
acq_cfg
,
ADC_CONF_ACQ_PRE_SAMP
,
&
pre
);
data
=
buf
->
data
;
/* Print data */
for
(
j
=
0
;
j
<
nsamples
;
j
++
)
{
for
(
j
=
0
;
j
<
buf
->
nsamples
;
j
++
)
{
if
(
(
n
>
0
&&
j
<
n
)
||
(
n
<
0
&&
(
nsamples
-
j
)
<=
(
-
n
))
)
{
(
n
<
0
&&
(
buf
->
nsamples
-
j
)
<=
(
-
n
))
)
{
printf
(
"%5i "
,
j
-
pre
);
for
(
ch
=
0
;
ch
<
nchan
;
ch
++
)
printf
(
"%7i"
,
*
(
data
++
));
for
(
ch
=
0
;
ch
<
nchan
;
ch
++
)
{
int32_t
sample
;
adc_buffer_get_sample
(
buf
,
ch
,
j
,
&
sample
);
printf
(
"%7i"
,
sample
);
}
printf
(
"
\n
"
);
}
else
{
data
+=
nchan
;
}
}
}
...
...
@@ -712,7 +867,7 @@ static int fald_acq_write_single(struct adc_buffer *buf)
char
fname
[
PATH_MAX
];
int16_t
*
data
;
/* FMC-ADC-100M sample size is 14bit, 16bit for ZIO */
FILE
*
f
;
int
err
=
0
;
int
err
=
0
,
n
;
sprintf
(
fname
,
"%s"
,
basefile
);
f
=
fopen
(
fname
,
"a"
);
...
...
@@ -727,7 +882,8 @@ static int fald_acq_write_single(struct adc_buffer *buf)
err
++
;
#endif
data
=
buf
->
data
;
if
(
fwrite
(
data
,
buf
->
samplesize
,
nsamples
,
f
)
!=
nsamples
)
n
=
fwrite
(
data
,
buf
->
samplesize
,
buf
->
nsamples
,
f
);
if
(
n
!=
buf
->
nsamples
)
err
++
;
if
(
err
)
{
...
...
@@ -752,6 +908,8 @@ static int fald_acq_write_multiple(struct adc_buffer *buf,
char
fname
[
PATH_MAX
];
uint16_t
*
data
;
FILE
*
f
;
int
n
;
#ifdef FIXME
/* FIXME zio control is specific. The library should provide
at least the metadata size so that automatic tools can at least
...
...
@@ -778,8 +936,8 @@ static int fald_acq_write_multiple(struct adc_buffer *buf,
return
-
1
;
}
data
=
buf
->
data
;
if
(
fwrite
(
data
,
buf
->
samplesize
,
nsamples
,
f
)
!=
nsamples
)
{
n
=
fwrite
(
data
,
buf
->
samplesize
,
buf
->
nsamples
,
f
);
if
(
n
!=
buf
->
nsamples
)
{
fprintf
(
stderr
,
"write(%s): short write
\n
"
,
fname
);
return
-
1
;
}
...
...
@@ -801,7 +959,7 @@ static void fald_acq_plot_data(struct adc_buffer *buf, unsigned int ch)
char
cmd
[
256
];
snprintf
(
fname
,
sizeof
(
fname
),
"/tmp/fmcadc.0x%04x.ch%d.dat"
,
devid
,
ch
);
if
(
write_file
(
fname
,
ch
,
data
,
(
nsamples
)
/
4
)
<
0
)
{
if
(
write_file
(
fname
,
ch
,
data
,
(
buf
->
nsamples
)
/
4
)
<
0
)
{
printf
(
"Cannot plot data. Write data into file %s failed.
\n
"
,
fname
);
return
;
}
...
...
@@ -813,6 +971,31 @@ static void fald_acq_plot_data(struct adc_buffer *buf, unsigned int ch)
system
(
cmd
);
}
/**
* It prints data using json format
*/
static
void
adc_acq_statistics
(
struct
adc_buffer
*
buf
,
unsigned
int
max_chan
)
{
int32_t
avg
;
int
i
,
err
;
fprintf
(
stdout
,
"{
\"
statistics
\"
: ["
);
for
(
i
=
0
;
i
<
max_chan
;
++
i
)
{
fprintf
(
stdout
,
"{
\"
chan
\"
: %d"
,
i
);
err
=
adc_buffer_math_avg
(
buf
,
i
,
&
avg
);
if
(
err
)
{
fprintf
(
stdout
,
",
\"
average
\"
: -1"
);
}
else
{
fprintf
(
stdout
,
",
\"
average
\"
: %"
PRId32
,
avg
);
}
if
(
i
==
max_chan
-
1
)
fprintf
(
stdout
,
"}"
);
else
fprintf
(
stdout
,
"}, "
);
}
fprintf
(
stdout
,
"]}
\n
"
);
}
/**
* It process the buffer
...
...
@@ -820,15 +1003,24 @@ static void fald_acq_plot_data(struct adc_buffer *buf, unsigned int ch)
* @param[in] iteration loop iteration number
*/
static
void
fald_acq_process_buffer
(
struct
adc_buffer
*
buf
,
struct
adc_conf
*
cfg_brd
,
struct
adc_conf
*
cfg_acq
,
unsigned
int
iteration
)
{
struct
adc_timestamp
ts
;
uint32_t
nchan
;
int
w
;
adc_tstamp_buffer
(
buf
,
&
ts
);
/* print/store data*/
switch
(
binmode
)
{
default:
fald_acq_print_data
(
buf
,
cfg_acq
,
show_ndata
);
fprintf
(
stderr
,
"time: {secs:%"
PRIu64
" ticks:%"
PRIu64
" bins:%"
PRIu64
"}
\n
"
,
ts
.
secs
,
ts
.
ticks
,
ts
.
bins
);
fald_acq_print_data
(
buf
,
cfg_brd
,
cfg_acq
,
show_ndata
);
break
;
case
1
:
fald_acq_write_single
(
buf
);
...
...
@@ -838,6 +1030,11 @@ static void fald_acq_process_buffer(struct adc_buffer *buf,
break
;
}
adc_get_conf
(
cfg_brd
,
ADC_CONF_BRD_N_CHAN
,
&
nchan
);
if
(
statistics
)
adc_acq_statistics
(
buf
,
nchan
);
if
(
arg_plot
)
for
(
w
=
0
;
w
<
nchan
;
++
w
)
fald_acq_plot_data
(
buf
,
w
+
1
);
...
...
@@ -886,9 +1083,6 @@ static int fald_acq_parse_args_basic(int argc, char *argv[])
}
memset
(
devname
,
0
,
len
);
strncpy
(
devname
,
optarg
,
len
-
1
);
fprintf
(
stdout
,
"%s: %s -> type:%s, devid:%s
\n
"
,
_argv
[
0
],
optarg
,
devname
,
strdevid
);
break
;
}
case
'V'
:
...
...
@@ -906,144 +1100,224 @@ static int fald_acq_parse_args_basic(int argc, char *argv[])
return
0
;
}
int
main
(
int
argc
,
char
*
argv
[])
/**
* Within this function we do a single acquisition
* @param[in] adc ADC device token
* @param[in] buf buffer to use
* @param[in] cfg_acq acquisition configuration
* @param[in] iteration acquisition number
* @return 0 on success
*/
static
int
adc_acq_acquisition
(
struct
adc_dev
*
adc
,
struct
adc_buffer
*
buf
,
struct
adc_conf
*
cfg_brd
,
struct
adc_conf
*
cfg_acq
,
unsigned
int
iteration
)
{
struct
adc_dev
*
adc
;
struct
adc_buffer
*
buf
;
struct
adc_timestamp
ts
;
struct
timeval
tv
=
{
0
,
0
};
struct
adc_conf
cfg_brd
,
cfg_acq
;
uint32_t
pre
,
post
,
bits
,
nshots
;
int
i
,
k
,
err
;
uint32_t
nshots
,
pre
,
post
;
int
err
,
k
;
fprintf
(
stderr
,
"Acquisition %i
\n
"
,
iteration
+
1
);
err
=
adc_init
();
if
(
err
)
exit
(
EXIT_FAILURE
);
/* Stop any pending acquisition */
err
=
adc_acq_stop
(
adc
,
0
);
if
(
err
)
{
fprintf
(
stderr
,
"Cannot stop pending acquisition: (%d) %s
\n
"
,
errno
,
adc_strerror
(
errno
));
goto
err
;
}
/* set local _argv[0] with pg name */
_argv
[
0
]
=
argv
[
0
];
err
=
fald_acq_parse_args_basic
(
argc
,
argv
);
if
(
err
)
exit
(
err
);
err
=
adc_acq_start
(
adc
,
ADC_F_FLUSH
,
&
tv
);
if
(
err
)
{
fprintf
(
stderr
,
"Cannot start acquisition: (%d) %s
\n
"
,
errno
,
adc_strerror
(
errno
));
goto
err
;
}
if
(
!
devname
)
{
fprintf
(
stderr
,
"%s: device is a mandatory argument (%s@0x%x)
\n
"
,
argv
[
0
],
devname
,
devid
);
fald_help
();
goto
err_devid_invalid
;
adc_get_conf
(
cfg_acq
,
ADC_CONF_ACQ_PRE_SAMP
,
&
pre
);
adc_get_conf
(
cfg_acq
,
ADC_CONF_ACQ_POST_SAMP
,
&
post
);
adc_get_conf
(
cfg_acq
,
ADC_CONF_ACQ_N_SHOTS
,
&
nshots
);
for
(
k
=
0
;
k
<
nshots
;
++
k
)
{
fprintf
(
stderr
,
"shot: %i/%i, nsamples: %d
\n
"
,
k
+
1
,
nshots
,
pre
+
post
);
if
(
arg_trgsw
)
{
sleep
(
arg_trgsw_delay
);
err
=
adc_trigger_fire
(
adc
);
if
(
err
)
goto
err
;
}
tv
.
tv_sec
=
60
;
err
=
adc_fill_buffer
(
adc
,
buf
,
fixup
?
ADC_F_FIXUP
:
0
,
&
tv
);
if
(
err
)
{
fprintf
(
stderr
,
"Failed to retrieve for data: (%d) %s
\n
"
,
errno
,
adc_strerror
(
errno
));
goto
err
;
}
fald_acq_process_buffer
(
buf
,
cfg_brd
,
cfg_acq
,
iteration
);
}
return
0
;
/* Open the ADC */
adc
=
adc_open
(
devname
,
devid
,
0
,
0
,
ADC_F_FLUSH
);
if
(
!
adc
)
{
fprintf
(
stderr
,
"%s: cannot open device: %s
\n
"
,
argv
[
0
],
adc_strerror
(
errno
));
goto
err_open
;
err:
return
err
;
}
/**
* Within this function we repeat the same acquisition a number of times
* @param[in] adc ADC device token
* @param[in] buf buffer to use
* @param[in] cfg_acq acquisition configuration
* @param[in] n number of acquisition to do
* @return 0 on success
*/
static
int
adc_acq_acquisition_n
(
struct
adc_dev
*
adc
,
struct
adc_conf
*
cfg_brd
,
struct
adc_conf
*
cfg_acq
,
unsigned
int
n
)
{
struct
adc_buffer
*
buf
;
uint32_t
pre
,
post
;
int
i
,
err
=
0
;
adc_get_conf
(
cfg_acq
,
ADC_CONF_ACQ_PRE_SAMP
,
&
pre
);
adc_get_conf
(
cfg_acq
,
ADC_CONF_ACQ_POST_SAMP
,
&
post
);
buf
=
adc_request_buffer
(
adc
,
pre
+
post
,
NULL
/* alloc */
,
0
);
if
(
!
buf
)
{
fprintf
(
stderr
,
"Cannot allocate buffer (%s)
\n
"
,
adc_strerror
(
errno
));
return
-
1
;
}
for
(
i
=
0
;
i
<
n
&&
arg_no_read
==
0
;
++
i
)
{
err
=
adc_acq_acquisition
(
adc
,
buf
,
cfg_brd
,
cfg_acq
,
i
);
if
(
err
)
break
;
}
adc_release_buffer
(
adc
,
buf
,
NULL
);
return
err
;
}
/**
* It configures an acquisition using user's parameters
* @param[in] adc ADC device token
* @param[in] argc number of parameters
* @param[in] argv user's parameters
* @param[out] cfg_acq acquisition configuration
* @return 0 on success
*/
static
int
adc_acq_configure
(
struct
adc_dev
*
adc
,
int
argc
,
char
*
argv
[],
struct
adc_conf
*
cfg_brd
,
struct
adc_conf
*
cfg_acq
)
{
int
err
=
0
;
/* get the new given trigger and acq config
Only the ones provided will override the current ones */
err
=
fald_acq_parse_args_and_configure
(
adc
,
argc
,
argv
);
if
(
err
)
goto
err_parse
;
return
err
;
/*
* W
e already know the configuration, but here we will retrieve it
*
from the library anyway as an example on how to get
the
*
configuration
* W
ithin this program we already know the configuration (we
*
applied it before), but here we will retrieve it from
the
*
library anyway because the library might have changed something
*/
memset
(
&
cfg_brd
,
0
,
sizeof
(
struct
adc_conf
));
cfg_brd
.
type
=
ADC_CONF_TYPE_BRD
;
adc_set_conf_mask_all
(
&
cfg_brd
,
adc
);
err
=
adc_retrieve_config
(
adc
,
&
cfg_brd
);
memset
(
cfg_brd
,
0
,
sizeof
(
struct
adc_conf
));
cfg_brd
->
type
=
ADC_CONF_TYPE_BRD
;
adc_set_conf_mask_all
(
cfg_brd
,
adc
);
err
=
adc_retrieve_config
(
adc
,
cfg_brd
);
if
(
err
)
{
fprintf
(
stderr
,
"Cannot retrive
the
board configuration: (%d) %s
\n
"
,
"Cannot retrive board configuration: (%d) %s
\n
"
,
errno
,
adc_strerror
(
errno
));
goto
err_conf_get
;
return
err
;
}
memset
(
&
cfg_acq
,
0
,
sizeof
(
struct
adc_conf
));
cfg_acq
.
type
=
ADC_CONF_TYPE_ACQ
;
adc_set_conf_mask_all
(
&
cfg_acq
,
adc
);
err
=
adc_retrieve_config
(
adc
,
&
cfg_acq
);
memset
(
cfg_acq
,
0
,
sizeof
(
struct
adc_conf
));
cfg_acq
->
type
=
ADC_CONF_TYPE_ACQ
;
adc_set_conf_mask_all
(
cfg_acq
,
adc
);
err
=
adc_retrieve_config
(
adc
,
cfg_acq
);
if
(
err
)
{
fprintf
(
stderr
,
"Cannot retrive
the
acquisition configuration: (%d) %s
\n
"
,
"Cannot retrive acquisition configuration: (%d) %s
\n
"
,
errno
,
adc_strerror
(
errno
));
goto
err_conf_get
;
return
err
;
}
adc_get_conf
(
&
cfg_brd
,
ADC_CONF_BRD_N_CHAN
,
&
nchan
);
adc_get_conf
(
&
cfg_acq
,
ADC_CONF_ACQ_PRE_SAMP
,
&
pre
);
adc_get_conf
(
&
cfg_acq
,
ADC_CONF_ACQ_POST_SAMP
,
&
post
);
adc_get_conf
(
&
cfg_acq
,
ADC_CONF_ACQ_N_BITS
,
&
bits
);
adc_get_conf
(
&
cfg_acq
,
ADC_CONF_ACQ_N_SHOTS
,
&
nshots
);
nsamples
=
pre
+
post
;
buf
=
adc_request_buffer
(
adc
,
nsamples
,
NULL
/* alloc */
,
0
);
if
(
!
buf
)
{
fprintf
(
stderr
,
"Cannot allocate buffer (%s)
\n
"
,
adc_strerror
(
errno
));
exit
(
1
);
}
return
err
;
}
for
(
i
=
0
;
i
<
loop
&&
arg_no_read
==
0
;
++
i
)
{
fprintf
(
stderr
,
"Acquisition %i
\n
"
,
i
+
1
);
/* Stop any pending acquisition */
err
=
adc_acq_stop
(
adc
,
0
);
if
(
err
)
{
fprintf
(
stderr
,
"Cannot stop pending acquisition: (%d) %s
\n
"
,
errno
,
adc_strerror
(
errno
));
continue
;
}
/**
* It cleans up our internal mess on exit
*/
static
void
adc_acq_cleanup
(
void
)
{
free
(
devname
);
}
err
=
adc_acq_start
(
adc
,
ADC_F_FLUSH
,
&
tv
);
if
(
err
)
{
fprintf
(
stderr
,
"Cannot start acquisition: (%d) %s
\n
"
,
errno
,
adc_strerror
(
errno
));
continue
;
}
for
(
k
=
0
;
k
<
nshots
;
++
k
)
{
fprintf
(
stderr
,
"iteration: %d, shot: %i/%i, nsamples: %d
\n
"
,
i
,
k
+
1
,
nshots
,
nsamples
);
int
main
(
int
argc
,
char
*
argv
[])
{
struct
adc_dev
*
adc
;
struct
adc_conf
cfg_acq
,
cfg_brd
;
int
err
;
err
=
adc_acq_poll
(
adc
,
0
,
NULL
);
if
(
err
)
{
fprintf
(
stderr
,
"Failed to wait for data: (%d) %s
\n
"
,
errno
,
adc_strerror
(
errno
));
break
;
}
atexit
(
adc_acq_cleanup
);
err
=
adc_fill_buffer
(
adc
,
buf
,
0
,
NULL
);
if
(
err
)
{
fprintf
(
stderr
,
"Failed to retrieve for data: (%d) %s
\n
"
,
errno
,
adc_strerror
(
errno
));
break
;
}
adc_tstamp_buffer
(
buf
,
&
ts
);
fprintf
(
stderr
,
"time: {secs:%"
PRIu64
" ticks:%"
PRIu64
" bins:%"
PRIu64
"}
\n
"
,
ts
.
secs
,
ts
.
ticks
,
ts
.
bins
);
/* set local _argv[0] with pg name */
_argv
[
0
]
=
argv
[
0
];
err
=
fald_acq_parse_args_basic
(
argc
,
argv
);
if
(
err
)
exit
(
err
);
fald_acq_process_buffer
(
buf
,
&
cfg_acq
,
i
);
}
if
(
!
devname
)
{
fprintf
(
stderr
,
"%s: device is a mandatory argument (%s@0x%x)
\n
"
,
argv
[
0
],
devname
,
devid
);
fald_help
();
goto
err_devid_invalid
;
}
adc_release_buffer
(
adc
,
buf
,
NULL
);
err
=
adc_init
();
if
(
err
)
exit
(
EXIT_FAILURE
);
/* Open the ADC */
adc
=
adc_open
(
devname
,
devid
,
0
,
0
,
ADC_F_FLUSH
);
if
(
!
adc
)
{
fprintf
(
stderr
,
"%s: cannot open device: %s
\n
"
,
argv
[
0
],
adc_strerror
(
errno
));
goto
err_open
;
}
err
=
adc_acq_configure
(
adc
,
argc
,
argv
,
&
cfg_brd
,
&
cfg_acq
);
if
(
err
)
goto
err_cfg
;
err
=
adc_acq_acquisition_n
(
adc
,
&
cfg_brd
,
&
cfg_acq
,
loop
);
if
(
err
)
goto
err_acq
;
adc_close
(
adc
);
adc_exit
();
free
(
devname
);
exit
(
EXIT_SUCCESS
);
err_
conf_get
:
err_
parse
:
err_
acq
:
err_
cfg
:
adc_close
(
adc
);
err_open:
err_devid_invalid:
adc_exit
();
free
(
devname
);
exit
(
EXIT_FAILURE
);
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment