work on ADC sampling, not tested, missing commands
This commit is contained in:
@@ -2,3 +2,270 @@
|
||||
// Created by MightyPork on 2018/02/04.
|
||||
//
|
||||
|
||||
#include "platform.h"
|
||||
#include "unit_base.h"
|
||||
|
||||
#define ADC_INTERNAL
|
||||
#include "_adc_internal.h"
|
||||
|
||||
void UADC_DMA_Handler(void *arg)
|
||||
{
|
||||
Unit *unit = arg;
|
||||
|
||||
assert_param(unit);
|
||||
struct priv *priv = unit->data;
|
||||
assert_param(priv);
|
||||
|
||||
const uint32_t isrsnapshot = priv->DMAx->ISR;
|
||||
|
||||
if (LL_DMA_IsActiveFlag_G(isrsnapshot, priv->dma_chnum)) {
|
||||
const bool tc = LL_DMA_IsActiveFlag_TC(isrsnapshot, priv->dma_chnum);
|
||||
const bool ht = LL_DMA_IsActiveFlag_HT(isrsnapshot, priv->dma_chnum);
|
||||
const bool te = LL_DMA_IsActiveFlag_TE(isrsnapshot, priv->dma_chnum);
|
||||
|
||||
// check what mode we're in
|
||||
const bool m_trig = priv->opmode == ADC_OPMODE_TRIGD;
|
||||
const bool m_stream = priv->opmode == ADC_OPMODE_STREAM;
|
||||
const bool m_fixcpt = priv->opmode == ADC_OPMODE_FIXCAPT;
|
||||
|
||||
if (m_trig || m_stream || m_fixcpt) {
|
||||
if (ht || tc) {
|
||||
const uint16_t start = priv->stream_startpos;
|
||||
uint16_t end;
|
||||
|
||||
if (ht) {
|
||||
dbg("HT");
|
||||
end = (uint16_t) (priv->dma_buffer_itemcount / 2);
|
||||
LL_DMA_ClearFlag_HT(priv->DMAx, priv->dma_chnum);
|
||||
}
|
||||
else {
|
||||
dbg("TC");
|
||||
end = (uint16_t) priv->dma_buffer_itemcount;
|
||||
LL_DMA_ClearFlag_TC(priv->DMAx, priv->dma_chnum);
|
||||
}
|
||||
|
||||
assert_param(start < end);
|
||||
|
||||
uint32_t sgcount = (end - start) / priv->nb_channels;
|
||||
|
||||
if (m_trig || m_fixcpt) {
|
||||
sgcount = MIN(priv->trig_stream_remain, sgcount);
|
||||
priv->trig_stream_remain -= sgcount;
|
||||
}
|
||||
|
||||
dbg("Would send %d groups (u16 offset %d -> %d)", (int)sgcount, (int)start, (int)(start+sgcount*priv->nb_channels));
|
||||
// TODO send the data together with remaining count (used to detect end of transmission)
|
||||
|
||||
if (m_trig || m_fixcpt) {
|
||||
if (priv->trig_stream_remain == 0) {
|
||||
dbg("End of capture");
|
||||
UADC_SwitchMode(unit, (priv->auto_rearm && m_trig) ? ADC_OPMODE_ARMED : ADC_OPMODE_IDLE);
|
||||
}
|
||||
}
|
||||
|
||||
if (end == priv->dma_buffer_itemcount) {
|
||||
priv->stream_startpos = 0;
|
||||
}
|
||||
else {
|
||||
priv->stream_startpos = end;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// This shouldn't happen, the interrupt should be disabled in this opmode
|
||||
dbg("(!) not streaming, DMA IT should be disabled");
|
||||
|
||||
if (ht) {
|
||||
LL_DMA_ClearFlag_HT(priv->DMAx, priv->dma_chnum);
|
||||
}
|
||||
else {
|
||||
LL_DMA_ClearFlag_TC(priv->DMAx, priv->dma_chnum);
|
||||
}
|
||||
}
|
||||
|
||||
if (te) {
|
||||
// this shouldn't happen - error
|
||||
dbg("ADC DMA TE!");
|
||||
LL_DMA_ClearFlag_TE(priv->DMAx, priv->dma_chnum);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UADC_ADC_EOS_Handler(void *arg)
|
||||
{
|
||||
uint64_t timestamp = PTIM_GetMicrotime();
|
||||
Unit *unit = arg;
|
||||
|
||||
dbg("ADC EOS ISR hit");
|
||||
assert_param(unit);
|
||||
struct priv *priv = unit->data;
|
||||
assert_param(priv);
|
||||
|
||||
// Wait for the DMA to complete copying the last sample
|
||||
while (priv->DMA_CHx->CNDTR % priv->nb_channels != 0);
|
||||
|
||||
uint32_t sample_pos;
|
||||
if (priv->DMA_CHx->CNDTR == 0) {
|
||||
sample_pos = (uint32_t) (priv->dma_buffer_itemcount - 1);
|
||||
} else {
|
||||
sample_pos = priv->DMA_CHx->CNDTR;
|
||||
}
|
||||
sample_pos -= priv->nb_channels;
|
||||
dbg("Sample pos %d", (int)sample_pos);
|
||||
|
||||
for (uint32_t i = 0; i < 18; i++) {
|
||||
if (priv->extended_channels_mask & (1 << i)) {
|
||||
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;
|
||||
}
|
||||
|
||||
if (priv->opmode == ADC_OPMODE_ARMED) {
|
||||
if (i == priv->trigger_source) {
|
||||
bool trigd = false;
|
||||
bool rising = false;
|
||||
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;
|
||||
}
|
||||
else if (priv->trig_prev_level > priv->trig_level && val <= priv->trig_level) {
|
||||
dbg("******** Falling edge");
|
||||
// Falling edge
|
||||
trigd = (bool) (priv->trig_edge & 0b10);
|
||||
}
|
||||
|
||||
if (trigd) {
|
||||
UADC_HandleTrigger(unit, rising, timestamp);
|
||||
}
|
||||
|
||||
priv->trig_prev_level = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dbg(" EOS ISR end.");
|
||||
}
|
||||
|
||||
void UADC_HandleTrigger(Unit *unit, bool rising, uint64_t timestamp)
|
||||
{
|
||||
assert_param(unit);
|
||||
struct priv *priv = unit->data;
|
||||
assert_param(priv);
|
||||
|
||||
if (priv->trig_holdoff != 0 && priv->trig_holdoff_remain > 0) {
|
||||
dbg("Trig discarded due to holdoff.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (priv->trig_holdoff > 0) {
|
||||
priv->trig_holdoff_remain = priv->trig_holdoff;
|
||||
// Start the tick
|
||||
unit->tick_interval = 1;
|
||||
unit->_tick_cnt = 0;
|
||||
}
|
||||
|
||||
dbg("Trigger condition hit, rising=%d", rising);
|
||||
// TODO Send pre-trigger
|
||||
|
||||
priv->stream_startpos = (uint16_t) priv->DMA_CHx->CNDTR;
|
||||
priv->trig_stream_remain = priv->trig_len;
|
||||
UADC_SwitchMode(unit, ADC_OPMODE_TRIGD);
|
||||
}
|
||||
|
||||
void UADC_updateTick(Unit *unit)
|
||||
{
|
||||
assert_param(unit);
|
||||
struct priv *priv = unit->data;
|
||||
assert_param(priv);
|
||||
|
||||
if (priv->trig_holdoff_remain > 0) {
|
||||
priv->trig_holdoff_remain--;
|
||||
|
||||
if (priv->trig_holdoff_remain == 0) {
|
||||
unit->tick_interval = 0;
|
||||
unit->_tick_cnt = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UADC_SwitchMode(Unit *unit, enum uadc_opmode new_mode)
|
||||
{
|
||||
assert_param(unit);
|
||||
struct priv *priv = unit->data;
|
||||
assert_param(priv);
|
||||
|
||||
if (new_mode == priv->opmode) return; // nothing to do
|
||||
|
||||
// if un-itied, can go only to IDLE
|
||||
assert_param((priv->opmode != ADC_OPMODE_UNINIT) || (new_mode == ADC_OPMODE_IDLE));
|
||||
|
||||
if (new_mode == ADC_OPMODE_UNINIT) {
|
||||
dbg("ADC switch -> UNINIT");
|
||||
// Stop the DMA, timer and disable ADC - this is called before tearing down the unit
|
||||
LL_TIM_DisableCounter(priv->TIMx);
|
||||
|
||||
// Switch off the ADC
|
||||
if (LL_ADC_IsEnabled(priv->ADCx)) {
|
||||
// Cancel ongoing conversion
|
||||
if (LL_ADC_REG_IsConversionOngoing(priv->ADCx)) {
|
||||
dbg("Stopping ADC conv");
|
||||
LL_ADC_REG_StopConversion(priv->ADCx);
|
||||
hw_wait_while(LL_ADC_REG_IsStopConversionOngoing(priv->ADCx), 100);
|
||||
}
|
||||
|
||||
LL_ADC_Disable(priv->ADCx);
|
||||
dbg("Disabling ADC");
|
||||
hw_wait_while(LL_ADC_IsDisableOngoing(priv->ADCx), 100);
|
||||
}
|
||||
|
||||
dbg("Disabling DMA");
|
||||
LL_DMA_DisableChannel(priv->DMAx, priv->dma_chnum);
|
||||
LL_DMA_DisableIT_HT(priv->DMAx, priv->dma_chnum);
|
||||
LL_DMA_DisableIT_TC(priv->DMAx, priv->dma_chnum);
|
||||
}
|
||||
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
|
||||
|
||||
// In IDLE, we don't need the DMA interrupts
|
||||
LL_DMA_DisableIT_HT(priv->DMAx, priv->dma_chnum);
|
||||
LL_DMA_DisableIT_TC(priv->DMAx, priv->dma_chnum);
|
||||
|
||||
// Use End Of Sequence to recover results for averaging from the DMA buffer and DR
|
||||
LL_ADC_EnableIT_EOS(priv->ADCx);
|
||||
|
||||
if (priv->opmode == ADC_OPMODE_UNINIT) {
|
||||
// Nothing is started yet - this is the only way to leave UNINIT
|
||||
LL_ADC_Enable(priv->ADCx);
|
||||
LL_DMA_EnableChannel(priv->DMAx, priv->dma_chnum);
|
||||
LL_TIM_EnableCounter(priv->TIMx);
|
||||
}
|
||||
}
|
||||
else if (new_mode == ADC_OPMODE_ARMED) {
|
||||
dbg("ADC switch -> ARMED");
|
||||
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");
|
||||
assert_param(priv->opmode == ADC_OPMODE_ARMED || priv->opmode == ADC_OPMODE_IDLE);
|
||||
|
||||
// during the capture, we disallow direct readout and averaging to reduce overhead
|
||||
LL_ADC_DisableIT_EOS(priv->ADCx);
|
||||
|
||||
// Enable the DMA buffer interrupts
|
||||
LL_DMA_EnableIT_HT(priv->DMAx, priv->dma_chnum);
|
||||
LL_DMA_EnableIT_TC(priv->DMAx, priv->dma_chnum);
|
||||
}
|
||||
|
||||
// the actual switch
|
||||
priv->opmode = new_mode;
|
||||
}
|
||||
|
||||
+42
-60
@@ -2,6 +2,7 @@
|
||||
// Created by MightyPork on 2018/02/03.
|
||||
//
|
||||
|
||||
#include <stm32f072xb.h>
|
||||
#include "platform.h"
|
||||
#include "unit_base.h"
|
||||
|
||||
@@ -20,50 +21,14 @@ 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;
|
||||
|
||||
return E_SUCCESS;
|
||||
}
|
||||
|
||||
static void UADC_DMA_Handler(void *arg)
|
||||
{
|
||||
Unit *unit = arg;
|
||||
|
||||
dbg("ADC DMA ISR hit");
|
||||
assert_param(unit);
|
||||
struct priv *priv = unit->data;
|
||||
assert_param(priv);
|
||||
|
||||
const uint32_t isrsnapshot = priv->DMAx->ISR;
|
||||
|
||||
if (LL_DMA_IsActiveFlag_G(isrsnapshot, priv->dma_chnum)) {
|
||||
bool tc = LL_DMA_IsActiveFlag_TC(isrsnapshot, priv->dma_chnum);
|
||||
bool ht = LL_DMA_IsActiveFlag_HT(isrsnapshot, priv->dma_chnum);
|
||||
|
||||
// Here we have to either copy it somewhere else, or notify another thread (queue?)
|
||||
// that the data is ready for reading
|
||||
|
||||
if (ht) {
|
||||
uint16_t start = 0;
|
||||
uint16_t end = (uint16_t) (priv->dma_buffer_size / 2);
|
||||
// TODO handle first half
|
||||
LL_DMA_ClearFlag_HT(priv->DMAx, priv->dma_chnum);
|
||||
}
|
||||
|
||||
if (tc) {
|
||||
uint16_t start = (uint16_t) (priv->dma_buffer_size / 2);
|
||||
uint16_t end = (uint16_t) priv->dma_buffer_size;
|
||||
// TODO handle second half
|
||||
LL_DMA_ClearFlag_TC(priv->DMAx, priv->dma_chnum);
|
||||
}
|
||||
|
||||
if (LL_DMA_IsActiveFlag_TE(isrsnapshot, priv->dma_chnum)) {
|
||||
// this shouldn't happen - error
|
||||
dbg("ADC DMA TE!");
|
||||
LL_DMA_ClearFlag_TE(priv->DMAx, priv->dma_chnum);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Finalize unit set-up */
|
||||
error_t UADC_init(Unit *unit)
|
||||
{
|
||||
@@ -133,6 +98,7 @@ error_t UADC_init(Unit *unit)
|
||||
// enable peripherals clock
|
||||
hw_periph_clock_enable(priv->ADCx);
|
||||
hw_periph_clock_enable(priv->TIMx);
|
||||
// DMA and GPIO clocks are enabled on startup automatically
|
||||
}
|
||||
|
||||
// ------------------- CONFIGURE THE TIMER --------------------------
|
||||
@@ -142,8 +108,7 @@ error_t UADC_init(Unit *unit)
|
||||
uint16_t presc;
|
||||
uint32_t count;
|
||||
float real_freq;
|
||||
if (!solve_timer(PLAT_APB1_HZ, priv->frequency, true, &presc, &count,
|
||||
&real_freq)) {
|
||||
if (!solve_timer(PLAT_APB1_HZ, priv->frequency, true, &presc, &count, &real_freq)) {
|
||||
dbg("Failed to resolve timer params.");
|
||||
return E_BAD_VALUE;
|
||||
}
|
||||
@@ -167,24 +132,29 @@ error_t UADC_init(Unit *unit)
|
||||
while (LL_ADC_IsCalibrationOnGoing(priv->ADCx)) {}
|
||||
dbg("ADC calibrated.");
|
||||
|
||||
uint32_t mask = 0;
|
||||
if (priv->enable_vref) mask |= LL_ADC_PATH_INTERNAL_VREFINT;
|
||||
if (priv->enable_tsense) mask |= LL_ADC_PATH_INTERNAL_TEMPSENSOR;
|
||||
LL_ADC_SetCommonPathInternalCh(priv->ADCx_Common, mask);
|
||||
{
|
||||
uint32_t mask = 0;
|
||||
if (priv->enable_vref) mask |= LL_ADC_PATH_INTERNAL_VREFINT;
|
||||
if (priv->enable_tsense) mask |= LL_ADC_PATH_INTERNAL_TEMPSENSOR;
|
||||
LL_ADC_SetCommonPathInternalCh(priv->ADCx_Common, mask);
|
||||
}
|
||||
|
||||
LL_ADC_SetDataAlignment(priv->ADCx, LL_ADC_DATA_ALIGN_RIGHT);
|
||||
LL_ADC_SetResolution(priv->ADCx, LL_ADC_RESOLUTION_12B);
|
||||
LL_ADC_REG_SetDMATransfer(priv->ADCx, LL_ADC_REG_DMA_TRANSFER_UNLIMITED);
|
||||
|
||||
// configure channels
|
||||
LL_ADC_REG_SetSequencerChannels(priv->ADCx, priv->channels);
|
||||
if (priv->enable_tsense) LL_ADC_REG_SetSequencerChAdd(priv->ADCx, LL_ADC_CHANNEL_TEMPSENSOR);
|
||||
if (priv->enable_vref) LL_ADC_REG_SetSequencerChAdd(priv->ADCx, LL_ADC_CHANNEL_VREFINT);
|
||||
priv->extended_channels_mask = priv->channels;
|
||||
if (priv->enable_tsense) priv->extended_channels_mask |= (1<<16);
|
||||
if (priv->enable_vref) priv->extended_channels_mask |= (1<<17);
|
||||
|
||||
priv->ADCx->CHSELR = priv->extended_channels_mask;
|
||||
|
||||
LL_ADC_REG_SetTriggerSource(priv->ADCx, LL_ADC_REG_TRIG_EXT_TIM15_TRGO);
|
||||
|
||||
LL_ADC_SetSamplingTimeCommonChannels(priv->ADCx, LL_ADC_SAMPLETIMES[priv->sample_time]);
|
||||
|
||||
LL_ADC_Enable(priv->ADCx);
|
||||
// LL_ADC_Enable(priv->ADCx);
|
||||
}
|
||||
|
||||
// --------------------- CONFIGURE DMA -------------------------------
|
||||
@@ -193,10 +163,14 @@ error_t UADC_init(Unit *unit)
|
||||
// The length must be a 2*multiple of the number of channels, in bytes
|
||||
uint16_t itemcount = (uint16_t) ((priv->nb_channels) * (uint16_t) (priv->buffer_size / (2 * priv->nb_channels)));
|
||||
if (itemcount % 2 == 1) itemcount -= priv->nb_channels;
|
||||
priv->dma_buffer_size = (uint16_t) (itemcount * 2);
|
||||
dbg("DMA item count is %d (%d bytes)", itemcount, priv->dma_buffer_size);
|
||||
priv->dma_buffer_itemcount = itemcount;
|
||||
|
||||
priv->dma_buffer = malloc_ck(priv->dma_buffer_size);
|
||||
dbg("DMA item count is %d (%d bytes), There are %d 2-byte samples per group.",
|
||||
priv->dma_buffer_itemcount,
|
||||
priv->dma_buffer_itemcount*sizeof(uint16_t),
|
||||
priv->nb_channels);
|
||||
|
||||
priv->dma_buffer = malloc_ck(priv->dma_buffer_itemcount * sizeof(uint16_t));
|
||||
if (NULL == priv->dma_buffer) return E_OUT_OF_MEM;
|
||||
assert_param(((uint32_t) priv->dma_buffer & 3) == 0); // must be aligned
|
||||
|
||||
@@ -218,21 +192,25 @@ error_t UADC_init(Unit *unit)
|
||||
|
||||
assert_param(SUCCESS == LL_DMA_Init(priv->DMAx, priv->dma_chnum, &init));
|
||||
|
||||
irqd_attach(priv->DMA_CHx, UADC_DMA_Handler, unit);
|
||||
// Interrupt on transfer 1/2 and complete
|
||||
// We will capture the first and second half and send it while the other half is being filled.
|
||||
LL_DMA_EnableIT_HT(priv->DMAx, priv->dma_chnum);
|
||||
LL_DMA_EnableIT_TC(priv->DMAx, priv->dma_chnum);
|
||||
// LL_DMA_EnableIT_HT(priv->DMAx, priv->dma_chnum);
|
||||
// LL_DMA_EnableIT_TC(priv->DMAx, priv->dma_chnum);
|
||||
}
|
||||
|
||||
LL_DMA_EnableChannel(priv->DMAx, priv->dma_chnum);
|
||||
}
|
||||
|
||||
dbg("ADC inited, starting the timer ...");
|
||||
// prepare the avg factor float for the ISR
|
||||
if (priv->averaging_factor > 1000) priv->averaging_factor = 1000; // normalize
|
||||
priv->avg_factor_as_float = priv->averaging_factor/1000.0f;
|
||||
|
||||
// FIXME - temporary demo - counter start...
|
||||
LL_ADC_REG_StartConversion(priv->ADCx); // the first conversion must be started manually
|
||||
LL_TIM_EnableCounter(priv->TIMx);
|
||||
dbg("ADC peripherals configured.");
|
||||
|
||||
irqd_attach(priv->DMA_CHx, UADC_DMA_Handler, unit);
|
||||
irqd_attach(priv->ADCx, UADC_ADC_EOS_Handler, unit);
|
||||
|
||||
UADC_SwitchMode(unit, ADC_OPMODE_IDLE);
|
||||
|
||||
return E_SUCCESS;
|
||||
}
|
||||
@@ -246,11 +224,15 @@ void UADC_deInit(Unit *unit)
|
||||
|
||||
// de-init peripherals
|
||||
if (unit->status == E_SUCCESS ) {
|
||||
UADC_SwitchMode(unit, ADC_OPMODE_UNINIT);
|
||||
|
||||
//LL_ADC_DeInit(priv->ADCx);
|
||||
LL_ADC_CommonDeInit(priv->ADCx_Common);
|
||||
LL_TIM_DeInit(priv->TIMx);
|
||||
|
||||
irqd_detach(priv->DMA_CHx, UADC_DMA_Handler);
|
||||
irqd_detach(priv->ADCx, UADC_ADC_EOS_Handler);
|
||||
|
||||
LL_DMA_DeInit(priv->DMAx, priv->dma_chnum);
|
||||
|
||||
free_ck(priv->dma_buffer);
|
||||
|
||||
+58
-17
@@ -11,35 +11,59 @@
|
||||
|
||||
#include "unit_base.h"
|
||||
|
||||
enum uadc_opmode {
|
||||
ADC_OPMODE_UNINIT, //!< Not yet switched to any mode
|
||||
ADC_OPMODE_IDLE, //!< Idle, each sample overwrites the previous. Allows immediate value readout and averaging.
|
||||
ADC_OPMODE_ARMED, //!< Armed for a trigger. Direct access and averaging are disabled.
|
||||
ADC_OPMODE_TRIGD, //!< Triggered, sending pre-trigger and streaming captured data.
|
||||
ADC_OPMODE_FIXCAPT,//!< Capture of fixed length without a trigger
|
||||
ADC_OPMODE_STREAM, //!< Unlimited capture
|
||||
};
|
||||
|
||||
/** Private data structure */
|
||||
struct priv {
|
||||
// settings
|
||||
uint16_t channels; //!< bit flags (will be recorded in order 0-15)
|
||||
bool enable_tsense; //!< append a signal from the temperature channel (voltage proportional to Tj)
|
||||
bool enable_vref; //!< append a signal from the internal voltage reference
|
||||
bool enable_vref; //!< append a signal from the internal voltage reference
|
||||
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
|
||||
|
||||
// TODO averaging (maybe a separate component?)
|
||||
// TODO threshold watchdog with hysteresis (maybe a separate component?)
|
||||
// TODO trigger level, edge direction, hold-off, pre-trigger buffer (extract from the DMA buffer)
|
||||
bool enable_averaging; //!< Enable exponential averaging
|
||||
uint16_t averaging_factor; //!< Exponential averaging factor 0-1000
|
||||
|
||||
// internal state
|
||||
ADC_TypeDef *ADCx;
|
||||
ADC_Common_TypeDef *ADCx_Common;
|
||||
TIM_TypeDef *TIMx;
|
||||
DMA_TypeDef *DMAx;
|
||||
uint8_t dma_chnum;
|
||||
DMA_Channel_TypeDef *DMA_CHx;
|
||||
uint16_t *dma_buffer;
|
||||
uint8_t nb_channels; // nr of enabled adc channels
|
||||
uint16_t dma_buffer_size; // real number of bytes
|
||||
};
|
||||
uint32_t extended_channels_mask; //!< channels bitfield including tsense and vref
|
||||
float avg_factor_as_float;
|
||||
ADC_TypeDef *ADCx; //!< The ADC peripheral used
|
||||
ADC_Common_TypeDef *ADCx_Common; //!< The ADC common control block
|
||||
TIM_TypeDef *TIMx; //!< ADC timing timer instance
|
||||
DMA_TypeDef *DMAx; //!< DMA isnatnce used
|
||||
uint8_t dma_chnum; //!< DMA channel number
|
||||
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)
|
||||
|
||||
// max size of the DMA buffer. The actual buffer size will be adjusted to accommodate
|
||||
// an even number of sample groups (sets of channels)
|
||||
#define UADC_DMA_MAX_BUF_LEN 512
|
||||
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.
|
||||
};
|
||||
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)
|
||||
};
|
||||
|
||||
/** Allocate data structure and set defaults */
|
||||
error_t UADC_preInit(Unit *unit);
|
||||
@@ -66,4 +90,21 @@ error_t UADC_init(Unit *unit);
|
||||
/** Tear down the unit */
|
||||
void UADC_deInit(Unit *unit);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/** DMA half/complete handler */
|
||||
void UADC_DMA_Handler(void *arg);
|
||||
|
||||
/** ADC eod of sequence handler */
|
||||
void UADC_ADC_EOS_Handler(void *arg);
|
||||
|
||||
/** Switch to a different opmode */
|
||||
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);
|
||||
|
||||
/** Handle a periodic tick - expiring the hold-off */
|
||||
void UADC_updateTick(Unit *unit);
|
||||
|
||||
#endif //GEX_F072_ADC_INTERNAL_H
|
||||
|
||||
@@ -83,15 +83,16 @@ void UADC_writeIni(Unit *unit, IniWriter *iw)
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
iw_comment(iw, "Enabled channels, comma separated");
|
||||
iw_comment(iw, "0-7 = A0-A7, 8-9 = B0-B1, 10-15 = C0-C5");
|
||||
iw_comment(iw, "0-7 = A0-A7, 8-9 = B0-B1, 10-15 = C0-C5");
|
||||
iw_entry(iw, "channels", "%s", pinmask2str_up(priv->channels, unit_tmp512));
|
||||
|
||||
iw_comment(iw, "Enable Tsense channel");
|
||||
iw_comment(iw, "Enable Tsense channel (#16)");
|
||||
iw_entry(iw, "enable_tsense", str_yn(priv->enable_tsense));
|
||||
|
||||
iw_comment(iw, "Enable Vref channel");
|
||||
iw_comment(iw, "Enable Vref channel (#17)");
|
||||
iw_entry(iw, "enable_vref", str_yn(priv->enable_tsense));
|
||||
|
||||
iw_cmt_newline(iw);
|
||||
iw_comment(iw, "Sampling time (0-7)");
|
||||
iw_entry(iw, "sample_time", "%d", (int)priv->sample_time);
|
||||
|
||||
@@ -103,5 +104,12 @@ void UADC_writeIni(Unit *unit, IniWriter *iw)
|
||||
iw_comment(iw, "- the buffer is shared by all channels");
|
||||
iw_comment(iw, "- insufficient buffer size can lead to data loss");
|
||||
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_entry(iw, "avg_factor", "%d", priv->averaging_factor);
|
||||
}
|
||||
|
||||
|
||||
@@ -40,4 +40,5 @@ const UnitDriver UNIT_ADC = {
|
||||
.deInit = UADC_deInit,
|
||||
// Function
|
||||
.handleRequest = UADC_handleRequest,
|
||||
.updateTick = UADC_updateTick,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user