experimental ADC front-end with dummy reports

adc
Ondřej Hruška 6 years ago
parent 02f69b0e37
commit 260fcc3e65
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 3
      framework/unit_registry.c
  2. 2
      units/1wire/_ow_api.c
  3. 75
      units/adc/_adc_core.c
  4. 1
      units/adc/_adc_init.c
  5. 36
      units/adc/_adc_internal.h
  6. 17
      units/adc/_adc_settings.c
  7. 186
      units/adc/unit_adc.c
  8. 2
      utils/error.h

@ -495,7 +495,8 @@ void ureg_deliver_unit_request(TF_Msg *msg)
if (rv == E_SUCCESS) {
if (confirmed) com_respond_ok(msg->frame_id);
}
else {
else if (rv != E_FAILURE) {
// Failure is returned when the handler already sent an error response.
com_respond_error(msg->frame_id, rv);
}
goto quit;

@ -125,5 +125,5 @@ error_t UU_1WIRE_Search(Unit *unit, bool with_alarm, bool restart,
return priv->searchState.error;
}
return E_FAILURE;
return E_INTERNAL_ERROR;
}

@ -8,6 +8,11 @@
#define ADC_INTERNAL
#include "_adc_internal.h"
void UADC_ReportEndOfStream(Unit *unit)
{
dbg("~~End Of Stream msg~~");
}
void UADC_DMA_Handler(void *arg)
{
Unit *unit = arg;
@ -59,6 +64,7 @@ void UADC_DMA_Handler(void *arg)
if (m_trig || m_fixcpt) {
if (priv->trig_stream_remain == 0) {
dbg("End of capture");
UADC_ReportEndOfStream(unit);
UADC_SwitchMode(unit, (priv->auto_rearm && m_trig) ? ADC_OPMODE_ARMED : ADC_OPMODE_IDLE);
}
}
@ -117,32 +123,31 @@ void UADC_ADC_EOS_Handler(void *arg)
uint16_t val = priv->dma_buffer[sample_pos];
dbg("Trig line level %d", (int)val);
if (priv->enable_averaging) {
priv->averaging_bins[i] =
priv->averaging_bins[i] * (1.0f - priv->avg_factor_as_float) +
((float) val) * priv->avg_factor_as_float;
} else {
priv->last_sample[i] = val;
}
priv->averaging_bins[i] =
priv->averaging_bins[i] * (1.0f - priv->avg_factor_as_float) +
((float) val) * priv->avg_factor_as_float;
priv->last_samples[i] = val;
if (priv->opmode == ADC_OPMODE_ARMED) {
if (i == priv->trigger_source) {
bool trigd = false;
bool rising = false;
uint8_t edge_type = 0;
if (priv->trig_prev_level < priv->trig_level && val >= priv->trig_level) {
dbg("******** Rising edge");
// Rising edge
trigd = (bool) (priv->trig_edge & 0b01);
rising = true;
edge_type = 1;
}
else if (priv->trig_prev_level > priv->trig_level && val <= priv->trig_level) {
dbg("******** Falling edge");
// Falling edge
trigd = (bool) (priv->trig_edge & 0b10);
edge_type = 2;
}
if (trigd) {
UADC_HandleTrigger(unit, rising, timestamp);
UADC_HandleTrigger(unit, edge_type, timestamp);
}
priv->trig_prev_level = val;
@ -154,7 +159,7 @@ void UADC_ADC_EOS_Handler(void *arg)
dbg(" EOS ISR end.");
}
void UADC_HandleTrigger(Unit *unit, bool rising, uint64_t timestamp)
void UADC_HandleTrigger(Unit *unit, uint8_t edge_type, uint64_t timestamp)
{
assert_param(unit);
struct priv *priv = unit->data;
@ -172,7 +177,7 @@ void UADC_HandleTrigger(Unit *unit, bool rising, uint64_t timestamp)
unit->_tick_cnt = 0;
}
dbg("Trigger condition hit, rising=%d", rising);
dbg("Trigger condition hit, edge=%d", edge_type);
// TODO Send pre-trigger
priv->stream_startpos = (uint16_t) priv->DMA_CHx->CNDTR;
@ -180,6 +185,44 @@ void UADC_HandleTrigger(Unit *unit, bool rising, uint64_t timestamp)
UADC_SwitchMode(unit, ADC_OPMODE_TRIGD);
}
void UADC_StartBlockCapture(Unit *unit, uint32_t len, TF_ID frame_id)
{
assert_param(unit);
struct priv *priv = unit->data;
assert_param(priv);
priv->stream_frame_id = frame_id;
priv->stream_startpos = (uint16_t) priv->DMA_CHx->CNDTR;
priv->trig_stream_remain = len;
UADC_SwitchMode(unit, ADC_OPMODE_FIXCAPT);
}
/** Start stream */
void UADC_StartStream(Unit *unit, TF_ID frame_id)
{
assert_param(unit);
struct priv *priv = unit->data;
assert_param(priv);
priv->stream_frame_id = frame_id;
priv->stream_startpos = (uint16_t) priv->DMA_CHx->CNDTR;
dbg("Start streaming.");
UADC_SwitchMode(unit, ADC_OPMODE_STREAM);
}
/** End stream */
void UADC_StopStream(Unit *unit)
{
assert_param(unit);
struct priv *priv = unit->data;
assert_param(priv);
dbg("Stop stream.");
UADC_ReportEndOfStream(unit);
UADC_SwitchMode(unit, ADC_OPMODE_IDLE);
}
/** Handle unit update tick - expire the trigger hold-off */
void UADC_updateTick(Unit *unit)
{
assert_param(unit);
@ -234,6 +277,7 @@ void UADC_SwitchMode(Unit *unit, enum uadc_opmode new_mode)
else if (new_mode == ADC_OPMODE_IDLE) {
dbg("ADC switch -> IDLE");
// IDLE and ARMED are identical with the exception that the trigger condition is not checked
// ARMED can be only entered from IDLE, thus we do the init only here.
// In IDLE, we don't need the DMA interrupts
LL_DMA_DisableIT_HT(priv->DMAx, priv->dma_chnum);
@ -254,8 +298,11 @@ void UADC_SwitchMode(Unit *unit, enum uadc_opmode new_mode)
assert_param(priv->opmode == ADC_OPMODE_IDLE);
// there's nothing else to do here
}
else if (new_mode == ADC_OPMODE_TRIGD || new_mode == ADC_OPMODE_STREAM) {
dbg("ADC switch -> TRIG'D or STREAM");
else if (new_mode == ADC_OPMODE_TRIGD ||
new_mode == ADC_OPMODE_STREAM ||
new_mode == ADC_OPMODE_FIXCAPT) {
dbg("ADC switch -> TRIG'D / STREAM / BLOCK");
assert_param(priv->opmode == ADC_OPMODE_ARMED || priv->opmode == ADC_OPMODE_IDLE);
// during the capture, we disallow direct readout and averaging to reduce overhead

@ -21,7 +21,6 @@ error_t UADC_preInit(Unit *unit)
priv->sample_time = 0b010; // 13.5c
priv->frequency = 1000;
priv->buffer_size = 512;
priv->enable_averaging = false;
priv->averaging_factor = 500;
priv->opmode = ADC_OPMODE_UNINIT;

@ -29,8 +29,6 @@ struct priv {
uint8_t sample_time; //!< 0-7 (corresponds to 1.5-239.5 cycles) - time for the sampling capacitor to charge
uint32_t frequency; //!< Timer frequency in Hz. Note: not all frequencies can be achieved accurately
uint16_t buffer_size; //!< Buffer size in bytes (count 2 bytes per channel per measurement) - faster sampling freq needs bigger buffer
bool enable_averaging; //!< Enable exponential averaging
uint16_t averaging_factor; //!< Exponential averaging factor 0-1000
// internal state
@ -44,25 +42,25 @@ struct priv {
DMA_Channel_TypeDef *DMA_CHx; //!< DMA channel instance
uint16_t *dma_buffer; //!< malloc'd buffer for the samples
uint8_t nb_channels; //!< nbr of enabled adc channels
uint16_t dma_buffer_itemcount; //!< real size of the buffer (adjusted from the configured size to evenly encompass 2*size of one sample)
uint16_t dma_buffer_itemcount; //!< real size of the buffer in samples (adjusted to fit 2x whole multiple of sample group)
uint32_t trig_stream_remain; //!< Counter of samples remaining to be sent in the post-trigger stream
uint16_t trig_holdoff_remain; //!< Tmp counter for the currently active hold-off
uint16_t trig_prev_level; //!< Value of the previous sample, used to detect trigger edge
uint16_t stream_startpos; //!< Byte offset in the DMA buffer where the next capture for a stream should start.
//!< Updated in TH/TC and on trigger (after the preceding data is sent as a pretrig buffer)
enum uadc_opmode opmode; //!< OpMode (state machine state)
union {
float averaging_bins[18]; //!< Averaging buffers, enough space to accommodate all channels (16 external + 2 internal)
uint16_t last_sample[18]; //!< If averaging is disabled, the last captured sample is stored here.
};
float averaging_bins[18]; //!< Averaging buffers, enough space to accommodate all channels (16 external + 2 internal)
uint16_t last_samples[18]; //!< If averaging is disabled, the last captured sample is stored here.
uint8_t trigger_source; //!< number of the pin selected as a trigger source
uint16_t pretrig_len; //!< Pre-trigger length, nbr of historical samples to report when trigger occurs
uint32_t trig_len; //!< Trigger length, nbr of samples to report AFTER a trigger occurs
uint16_t trig_level; //!< Triggering level in LSB
uint16_t trig_prev_level; //!< Value of the previous sample, used to detect trigger edge
uint8_t trig_edge; //!< Which edge we want to trigger on. 1-rising, 2-falling, 3-both
uint32_t trig_stream_remain; //!< Counter of samples remaining to be sent in the post-trigger stream
bool auto_rearm; //!< Flag that the trigger should be re-armed after the stream finishes
uint16_t trig_holdoff; //!< Trigger hold-off time, set when configuring the trigger
uint16_t trig_holdoff_remain; //!< Tmp counter for the currently active hold-off
uint16_t stream_startpos; //!< Byte offset in the DMA buffer where the next capture for a stream should start.
//!< Updated in TH/TC and on trigger (after the preceding data is sent as a pretrig buffer)
TF_ID stream_frame_id; //!< Session ID for multi-part stream (response or report)
};
/** Allocate data structure and set defaults */
@ -102,9 +100,21 @@ void UADC_ADC_EOS_Handler(void *arg);
void UADC_SwitchMode(Unit *unit, enum uadc_opmode new_mode);
/** Handle trigger - process pre-trigger and start streaming the requested number of samples */
void UADC_HandleTrigger(Unit *unit, bool rising, uint64_t timestamp);
void UADC_HandleTrigger(Unit *unit, uint8_t edge_type, uint64_t timestamp);
/** Handle a periodic tick - expiring the hold-off */
void UADC_updateTick(Unit *unit);
/** Send a end-of-stream message to PC's stream listener so it can shut down. */
void UADC_ReportEndOfStream(Unit *unit);
/** Start a block capture */
void UADC_StartBlockCapture(Unit *unit, uint32_t len, TF_ID frame_id);
/** Start stream */
void UADC_StartStream(Unit *unit, TF_ID frame_id);
/** End stream */
void UADC_StopStream(Unit *unit);
#endif //GEX_F072_ADC_INTERNAL_H

@ -25,6 +25,9 @@ void UADC_loadBinary(Unit *unit, PayloadParser *pp)
if (version >= 1) {
priv->buffer_size = pp_u16(pp);
}
if (version >= 2) {
priv->averaging_factor = pp_u16(pp);
}
}
/** Write to a binary buffer for storing in Flash */
@ -32,7 +35,7 @@ void UADC_writeBinary(Unit *unit, PayloadBuilder *pb)
{
struct priv *priv = unit->data;
pb_u8(pb, 1); // version
pb_u8(pb, 2); // version
pb_u16(pb, priv->channels);
pb_bool(pb, priv->enable_tsense);
@ -40,6 +43,7 @@ void UADC_writeBinary(Unit *unit, PayloadBuilder *pb)
pb_u8(pb, priv->sample_time);
pb_u32(pb, priv->frequency);
pb_u16(pb, priv->buffer_size);
pb_u16(pb, priv->averaging_factor);
}
// ------------------------------------------------------------------------
@ -69,6 +73,10 @@ error_t UADC_loadIni(Unit *unit, const char *key, const char *value)
else if (streq(key, "buffer_size")) {
priv->buffer_size = (uint16_t) avr_atoi(value);
}
else if (streq(key, "avg_factor")) {
priv->averaging_factor = (uint16_t) avr_atoi(value);
if (priv->averaging_factor > 1000) return E_BAD_VALUE;
}
else {
return E_BAD_KEY;
}
@ -106,10 +114,9 @@ void UADC_writeIni(Unit *unit, IniWriter *iw)
iw_entry(iw, "buffer_size", "%d", (int)priv->buffer_size);
iw_cmt_newline(iw);
iw_comment(iw, "Enable exponential averaging (only when not streaming)");
iw_comment(iw, "Used formula: y[t]=(1-k)*y[t-1]+k*u[t]");
iw_entry(iw, "averaging", str_yn(priv->enable_averaging));
iw_comment(iw, "Averaging factor k (permil, range 0-1000 ~ 0.000-1.000)");
iw_comment(iw, "Exponential averaging coefficient (permil, range 0-1000 ~ 0.000-1.000)");
iw_comment(iw, "- used formula: y[t]=(1-k)*y[t-1]+k*u[t]");
iw_comment(iw, "- available only for direct readout (i.e. not used in block capture)");
iw_entry(iw, "avg_factor", "%d", priv->averaging_factor);
}

@ -11,13 +11,195 @@
// ------------------------------------------------------------------------
enum TplCmd_ {
CMD_DUMMY,
CMD_READ_RAW = 0,
CMD_READ_SMOOTHED = 1,
CMD_GET_ENABLED_CHANNELS = 10,
CMD_SETUP_TRIGGER = 20,
CMD_ARM = 21,
CMD_DISARM = 22,
CMD_ABORT = 23, // abort any ongoing capture or stream
CMD_FORCE_TRIGGER = 24,
CMD_BLOCK_CAPTURE = 25,
CMD_STREAM_START = 26,
CMD_STREAM_STOP = 27,
};
/** Handle a request message */
static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, PayloadParser *pp)
{
struct priv *priv = unit->data;
PayloadBuilder pb = pb_start(unit_tmp512, UNIT_TMP_LEN, NULL);
switch (command) {
case CMD_GET_ENABLED_CHANNELS:
dbg("> Query channels");
for (uint8_t i = 0; i < 18; i++) {
if (priv->extended_channels_mask & (1 << i)) {
pb_u8(&pb, i);
}
}
com_respond_pb(frame_id, MSG_SUCCESS, &pb);
return E_SUCCESS;
case CMD_READ_RAW:
dbg("> Read raw");
if(priv->opmode != ADC_OPMODE_IDLE && priv->opmode != ADC_OPMODE_ARMED) {
return E_BUSY;
}
for (uint8_t i = 0; i < 18; i++) {
if (priv->extended_channels_mask & (1 << i)) {
pb_u8(&pb, i);
pb_u16(&pb, priv->last_samples[i]);
}
}
assert_param(pb.ok);
com_respond_pb(frame_id, MSG_SUCCESS, &pb);
return E_SUCCESS;
case CMD_READ_SMOOTHED:
dbg("> Read smoothed");
if(priv->opmode != ADC_OPMODE_IDLE && priv->opmode != ADC_OPMODE_ARMED) {
return E_BUSY;
}
for (uint8_t i = 0; i < 18; i++) {
if (priv->extended_channels_mask & (1 << i)) {
pb_u8(&pb, i);
pb_float(&pb, priv->averaging_bins[i]);
}
}
assert_param(pb.ok);
com_respond_pb(frame_id, MSG_SUCCESS, &pb);
return E_SUCCESS;
case CMD_SETUP_TRIGGER:
dbg("> Setup trigger");
if(priv->opmode != ADC_OPMODE_IDLE) return E_BUSY;
{
uint8_t source = pp_u8(pp);
uint16_t level = pp_u16(pp);
uint8_t edge = pp_u8(pp);
uint16_t pretrig = pp_u16(pp);
uint32_t count = pp_u32(pp);
uint16_t holdoff = pp_u16(pp);
bool auto_rearm = pp_bool(pp);
if (source > 17) {
com_respond_str(MSG_ERROR, frame_id, "Invalid trig source");
return E_FAILURE;
}
if (0 == (priv->extended_channels_mask & (1 << source))) {
com_respond_str(MSG_ERROR, frame_id, "Channel not enabled");
return E_FAILURE;
}
if (level > 4095) {
com_respond_str(MSG_ERROR, frame_id, "Level out of range (0-4095)");
return E_FAILURE;
}
if (edge == 0 || edge > 3) {
com_respond_str(MSG_ERROR, frame_id, "Bad edge");
return E_FAILURE;
}
// XXX the max size may be too much
uint16_t max_pretrig = (priv->dma_buffer_itemcount / priv->nb_channels);
if (pretrig > max_pretrig) {
com_respond_snprintf(frame_id, MSG_ERROR,
"Pretrig too large (max %d)", (int) max_pretrig);
return E_FAILURE;
}
priv->trigger_source = source;
priv->trig_level = level;
priv->trig_prev_level = priv->last_samples[source];
priv->trig_edge = edge;
priv->pretrig_len = pretrig;
priv->trig_len = count;
priv->trig_holdoff = holdoff;
priv->auto_rearm = auto_rearm;
}
return E_SUCCESS;
case CMD_ARM:
dbg("> Arm");
if(priv->opmode != ADC_OPMODE_IDLE) return E_BUSY;
if (priv->trig_len == 0) {
com_respond_str(MSG_ERROR, frame_id, "Trigger not configured.");
return E_FAILURE;
}
UADC_SwitchMode(unit, ADC_OPMODE_ARMED);
return E_SUCCESS;
case CMD_DISARM:
dbg("> Disarm");
if(priv->opmode == ADC_OPMODE_IDLE) {
return E_SUCCESS; // already idle, success - no work to do
}
// capture in progress
if(priv->opmode != ADC_OPMODE_ARMED) return E_BUSY;
UADC_SwitchMode(unit, ADC_OPMODE_IDLE);
return E_SUCCESS;
case CMD_ABORT:;
dbg("> Abort capture");
enum uadc_opmode old_opmode = priv->opmode;
UADC_SwitchMode(unit, ADC_OPMODE_IDLE);
if (old_opmode == ADC_OPMODE_FIXCAPT ||
old_opmode == ADC_OPMODE_STREAM ||
old_opmode == ADC_OPMODE_TRIGD) {
UADC_ReportEndOfStream(unit);
}
return E_SUCCESS;
case CMD_FORCE_TRIGGER:
dbg("> Force trigger");
if(priv->opmode == ADC_OPMODE_IDLE) {
com_respond_str(MSG_ERROR, frame_id, "Not armed");
return E_FAILURE;
}
if(priv->opmode != ADC_OPMODE_ARMED) return E_BUSY;
UADC_HandleTrigger(unit, 0b11, PTIM_GetMicrotime());
return E_SUCCESS;
case CMD_BLOCK_CAPTURE:
dbg("> Block cpt");
if(priv->opmode != ADC_OPMODE_ARMED &&
priv->opmode != ADC_OPMODE_IDLE) return E_BUSY;
uint32_t count = pp_u32(pp);
UADC_StartBlockCapture(unit, count, frame_id);
return E_SUCCESS;
case CMD_STREAM_START:
dbg("> Stream ON");
if(priv->opmode != ADC_OPMODE_ARMED &&
priv->opmode != ADC_OPMODE_IDLE) return E_BUSY;
UADC_StartStream(unit, frame_id);
return E_SUCCESS;
case CMD_STREAM_STOP:
dbg("> Stream OFF");
if(priv->opmode != ADC_OPMODE_STREAM) {
com_respond_str(MSG_ERROR, frame_id, "Not streaming");
return E_FAILURE;
}
UADC_StopStream(unit);
return E_SUCCESS;
default:
return E_UNKNOWN_COMMAND;
}
@ -28,7 +210,7 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P
/** Unit template */
const UnitDriver UNIT_ADC = {
.name = "ADC",
.description = "Analog inputs",
.description = "Analog/Digital converter",
// Settings
.preInit = UADC_preInit,
.cfgLoadBinary = UADC_loadBinary,

@ -13,7 +13,7 @@
#define X_ERROR_CODES \
/* Shared errors */ \
X(SUCCESS, NULL) /* operation succeeded / unit loaded. Must be 0 */ \
X(FAILURE, NULL) /* generic error */ \
X(FAILURE, NULL) /* generic error. If returned from a unit handler, does NOT generate a response. */ \
X(INTERNAL_ERROR, NULL) /* a bug */ \
X(LOADING, NULL) /* unit is loading */ \
X(UNKNOWN_COMMAND, NULL) \

Loading…
Cancel
Save