Commit d3b68357 authored by Federico Vaga's avatar Federico Vaga

Merge branch 'feature/zero-offset' into develop

parents 8ba919e1 443e1442
......@@ -207,7 +207,34 @@ and get values with :cpp:func:`adc_get_param()`
err = adc_set_param(adc, "identifier", NULL, &v_int);
err = adc_get_param(adc, "identifier", NULL, &v_int);
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, 2, 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>`.
.. _`lib:usr:cfg:cus`:
Board Specific Configuration
''''''''''''''''''''''''''''
......@@ -255,6 +282,10 @@ 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
Acquisition
-----------
......
......@@ -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
......
......@@ -23,6 +23,16 @@ extern "C" {
#include "adc-lib.h"
/**
* 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,
};
/**
* List of possible buffer types (options for ADC_CONF_100M14B4CHA_BUF_TYPE)
......
......@@ -47,6 +47,7 @@ struct adc_operations {
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 */
};
......@@ -183,6 +184,12 @@ 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 int chan,
unsigned int nsamples,
unsigned long flags,
int32_t *offset);
/*adc-genericfake*/
......
......@@ -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
......@@ -269,6 +275,24 @@ struct adc_conf {
#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_MASK=(ADC_OFFSET_AC_F_MANUAL |
ADC_OFFSET_AC_F_RESTORE |
ADC_OFFSET_AC_F_SOFTWARE), /**< used internally */
};
/**
* @defgroup dev Basic
* Basic library functions
......@@ -290,6 +314,9 @@ 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 int chan,
unsigned long flags);
/**@}*/
......@@ -406,6 +433,16 @@ extern int adc_buffer_get_sample(struct adc_buffer *buf,
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 */
extern const char * const libadc_version_s;
......
......@@ -36,6 +36,8 @@
#define ADC_CONF_GET 0
#define ADC_CONF_SET 1
#define __ADC_CONF_CHN_OFFSET_ZERO 200
static typeof(adc_get_param) *adc_param[] = {
[ADC_CONF_GET] = adc_get_param,
[ADC_CONF_SET] = adc_set_param,
......@@ -335,6 +337,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;
......@@ -734,6 +739,294 @@ static int adc_100m14b4cha_buffer_get_sample(struct adc_buffer *buf,
return 0;
}
struct tmp_cfg_store {
uint32_t trg_source;
uint32_t pre;
uint32_t post;
uint32_t nshots;
uint32_t undersample;
uint32_t chan;
uint32_t range;
uint32_t termination;
uint32_t saturation;
uint32_t offset;
};
static int __cfg_offac_save(struct adc_dev *dev,
unsigned int chan,
struct tmp_cfg_store *cfg,
unsigned long flags)
{
int err;
if ((flags & ADC_OFFSET_AC_F_MANUAL) ||
!(flags & ADC_OFFSET_AC_F_RESTORE))
return 0;
cfg->chan = chan;
err = adc_100m14b4cha_config_acq(dev, ADC_CONF_ACQ_PRE_SAMP,
&cfg->pre, ADC_CONF_GET);
if (err)
goto err;
err = adc_100m14b4cha_config_acq(dev, ADC_CONF_ACQ_POST_SAMP,
&cfg->post, ADC_CONF_GET);
if (err)
goto err;
err = adc_100m14b4cha_config_acq(dev, ADC_CONF_ACQ_N_SHOTS,
&cfg->nshots, ADC_CONF_GET);
if (err)
goto err;
err = adc_100m14b4cha_config_acq(dev, ADC_CONF_ACQ_UNDERSAMPLE,
&cfg->undersample, ADC_CONF_GET);
if (err)
goto err;
err = adc_100m14b4cha_config_chn(dev, cfg->chan,
ADC_CONF_CHN_RANGE,
&cfg->range, ADC_CONF_GET);
if (err)
goto err;
err = adc_100m14b4cha_config_chn(dev, cfg->chan,
ADC_CONF_CHN_TERMINATION,
&cfg->termination, ADC_CONF_GET);
if (err)
goto err;
err = adc_100m14b4cha_config_chn(dev, cfg->chan,
ADC_CONF_CHN_SATURATION,
&cfg->saturation, ADC_CONF_GET);
if (err)
goto err;
err = adc_100m14b4cha_config_chn(dev, cfg->chan,
ADC_CONF_CHN_OFFSET,
&cfg->offset, ADC_CONF_GET);
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_100m14b4cha_config_acq(dev, ADC_CONF_ACQ_PRE_SAMP,
&cfg->pre, ADC_CONF_SET);
err |= adc_100m14b4cha_config_acq(dev, ADC_CONF_ACQ_POST_SAMP,
&cfg->post, ADC_CONF_SET);
err |= adc_100m14b4cha_config_acq(dev, ADC_CONF_ACQ_N_SHOTS,
&cfg->nshots, ADC_CONF_SET);
err |= adc_100m14b4cha_config_acq(dev, ADC_CONF_ACQ_UNDERSAMPLE,
&cfg->undersample, ADC_CONF_SET);
err |= adc_100m14b4cha_config_chn(dev, cfg->chan,
ADC_CONF_CHN_RANGE,
&cfg->range, ADC_CONF_SET);
err |= adc_100m14b4cha_config_chn(dev, cfg->chan,
ADC_CONF_CHN_TERMINATION,
&cfg->termination, ADC_CONF_SET);
err |= adc_100m14b4cha_config_chn(dev, cfg->chan,
ADC_CONF_CHN_SATURATION,
&cfg->saturation, ADC_CONF_SET);
err |= adc_100m14b4cha_config_chn(dev, cfg->chan,
ADC_CONF_CHN_OFFSET,
&cfg->offset, ADC_CONF_SET);
err |= adc_set_param(dev, "cset0/trigger/source", NULL,
(int *)&cfg->trg_source);
if (err)
errno = ADC_OFF_AC_RESTORE_R;
return err;
}
#define CFG_OFFAC_NSAMPLES 100000
static struct adc_conf cfg_off_acq = {
.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,
},
};
static struct adc_conf cfg_off_cus = {
.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,
},
};
/**
* 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 int chan,
unsigned long flags)
{
int err, trgsrc = FA100M14B4C_TRG_SRC_SW;
uint32_t range = 0, offset = 0;
if (flags & ADC_OFFSET_AC_F_MANUAL)
return 0;
err = adc_set_param(dev, "cset0/trigger/source", NULL,
&trgsrc);
if (err)
return -1;
err = adc_apply_config(dev, 0 , &cfg_off_acq);
if (err)
return -1;
err = adc_apply_config(dev, 0 , &cfg_off_cus);
if (err)
return -1;
err = adc_100m14b4cha_config_chn(dev, chan,
ADC_CONF_CHN_RANGE,
&range, ADC_CONF_SET);
if (err)
return -1;
err = adc_100m14b4cha_config_chn(dev, chan,
ADC_CONF_CHN_OFFSET,
&offset, ADC_CONF_SET);
if (err)
return -1;
err = adc_100m14b4cha_config_chn(dev, chan,
__ADC_CONF_CHN_OFFSET_ZERO,
&offset, ADC_CONF_SET);
if (err)
return -1;
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 0x45:
range_uV = 10000000;
break;
case 0x23:
range_uV = 100000;
break;
case 0x11:
case 0x00:
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 adc_100m14b4cha_offset_auto_clear(struct adc_dev *dev,
unsigned int chan,
unsigned long flags)
{
struct tmp_cfg_store tmpcfg;
int32_t offset;
int err, err_rst;
uint32_t range;
flags |= ADC_OFFSET_AC_F_SOFTWARE;
err = __cfg_offac_save(dev, chan, &tmpcfg, flags);
if (err)
return err;
err = __cfg_offac_apply(dev, chan, flags);
if (err)
goto err;
err = adc_offset_auto_clear_sw_avg(dev, chan, CFG_OFFAC_NSAMPLES,
flags,
&offset);
if (err)
goto err;
err = adc_100m14b4cha_config_chn(dev, chan, ADC_CONF_CHN_RANGE,
&range, ADC_CONF_GET);
if (err)
goto err;
err_rst = __cfg_offac_restore(dev, &tmpcfg, flags);
if (err_rst) {
errno = ADC_OFF_AC_RESTORE_R;
return err_rst;
}
/*
* 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
*/
offset = __convert_hw_to_uv(offset, range);
return adc_100m14b4cha_config_chn(dev, chan,
__ADC_CONF_CHN_OFFSET_ZERO,
(uint32_t *)&offset, ADC_CONF_SET);
err:
err_rst = __cfg_offac_restore(dev, &tmpcfg, flags);
if (err_rst) {
errno = ADC_OFF_AC_RESTORE_R;
return err_rst;
}
return err;
}
#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) | \
......@@ -785,6 +1078,7 @@ static struct adc_operations fa_100ms_4ch_14bit_op = {
.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 = {
......
/*
* 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;
}
......@@ -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,72 @@ uint64_t adc_get_capabilities(struct adc_dev *dev,
return b->board->capabilities[type];
}
/**
* It computes what is the necessary offset to apply on a given channel
* in order to clear a constant offset on a channel
* approach.
* @param[in] dev ADC device token
* @param[in] chan channel number
* @param[in] flags options @see adc_offset_auto_clear_flags
* @param[out] offset compensation offset
* @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.
*/
int adc_offset_auto_clear_sw_avg(struct adc_dev *dev,
unsigned int chan,
unsigned int nsamples,
unsigned long flags,
int32_t *offset)
{
struct adc_buffer *buf;
struct timeval tv = {0, 0};
int err, err_stop;
if (!(flags & ADC_OFFSET_AC_F_SOFTWARE)) {
errno = EINVAL;
return -1;
}
if (!adc_has_trigger_fire(dev)) {
errno = ADC_ENOP_SWTRG;
return -1;
}
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;
err = adc_buffer_math_avg(buf, chan, offset);
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;
}
......@@ -508,6 +508,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)
{
......@@ -515,7 +516,7 @@ 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);
......@@ -568,3 +569,50 @@ int adc_buffer_fixup(struct adc_buffer *buf)
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 aventual offsets on a given channel
* @param[in] dev ADC device token
* @param[in] chan channel number
* @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 int chan,
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, chan, flags);
}
......@@ -22,6 +22,7 @@
#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;
......@@ -29,6 +30,8 @@ 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;
......@@ -51,6 +54,7 @@ static void fald_help()
printf(" <NOT IMPLEMENTED YET>\n");
printf(" --trg-sw <parameters> configure a software trigger\n");
printf(" <delay_seconds>\n");
printf(" --off-clr <mode>,<idx> run offset-clear on a given channel (mode: {a: automatic, m: manual})\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");
......@@ -64,6 +68,7 @@ static void fald_help()
">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(" --stats It prints some statistics\n");
printf(" --version|-V print version information\n");
printf(" --help|-h show this help\n\n");
}
......@@ -74,6 +79,7 @@ enum fald_acq_options {
FALD_ACQ_OPT_TRG_THR,
FALD_ACQ_OPT_TRG_TIM,
FALD_ACQ_OPT_TRG_SW,
FALD_ACQ_OPT_OFF_CLR,
};
static struct option options[] = {
......@@ -85,6 +91,9 @@ static struct option options[] = {
{"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 */
......@@ -448,15 +457,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;
}
......@@ -626,6 +635,58 @@ 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 int chan;
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,%d",
&offclr_mode, &chan);
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;
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, chan, flags);
if (ret < 0)
return ret;
break;
default:
break;
}
}
return 0;
}
/**
* It parses command line arguments and consequential it configures the device
......@@ -712,6 +773,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;
......@@ -869,6 +934,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
......@@ -894,6 +984,9 @@ static void fald_acq_process_buffer(struct adc_buffer *buf,
break;
}
if (statistics)
adc_acq_statistics(buf, nchan);
if (arg_plot)
for (w = 0; w < nchan; ++w)
fald_acq_plot_data(buf, w + 1);
......@@ -1079,7 +1172,9 @@ int main(int argc, char *argv[])
break;
}
err = adc_fill_buffer(adc, buf, 0, NULL);
err = adc_fill_buffer(adc, buf,
fixup ? ADC_F_FIXUP: 0,
NULL);
if (err) {
fprintf(stderr, "Failed to retrieve for data: (%d) %s\n",
errno, adc_strerror(errno));
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment