work on ADC sampling, not tested, missing commands

adc
Ondřej Hruška 7 years ago
parent 300a6a6e90
commit 02f69b0e37
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 3
      platform/hw_utils.c
  2. 12
      platform/hw_utils.h
  3. 13
      platform/irq_dispatcher.c
  4. 5
      platform/plat_init.c
  5. 267
      units/adc/_adc_core.c
  6. 94
      units/adc/_adc_init.c
  7. 73
      units/adc/_adc_internal.h
  8. 12
      units/adc/_adc_settings.c
  9. 1
      units/adc/unit_adc.c
  10. 6
      utils/malloc_safe.c

@ -349,6 +349,9 @@ bool solve_timer(uint32_t base_freq, uint32_t required_freq, bool is16bit,
uint16_t *presc, uint32_t *count, float *real_freq) uint16_t *presc, uint32_t *count, float *real_freq)
{ {
if (required_freq == 0) return false; if (required_freq == 0) return false;
// XXX consider using the LL macros __LL_TIM_CALC_PSC and __LL_TIM_CALC_ARR
const float fPresc = base_freq / required_freq; const float fPresc = base_freq / required_freq;
uint32_t wCount = (uint32_t) lrintf(fPresc); uint32_t wCount = (uint32_t) lrintf(fPresc);

@ -183,4 +183,16 @@ void hw_periph_clock_disable(void *periph);
bool solve_timer(uint32_t base_freq, uint32_t required_freq, bool is16bit, bool solve_timer(uint32_t base_freq, uint32_t required_freq, bool is16bit,
uint16_t *presc, uint32_t *count, float *real_freq); uint16_t *presc, uint32_t *count, float *real_freq);
#define hw_wait_while(call, timeout) \
do { \
uint32_t _ts = HAL_GetTick(); \
while (1 == (call)) { \
if (HAL_GetTick() - _ts > (timeout)) { \
trap("Timeout"); \
} \
} \
} while (0)
#define hw_wait_until(call, timeout) hw_wait_while(!(call), (timeout))
#endif //GEX_PIN_UTILS_H #endif //GEX_PIN_UTILS_H

@ -65,6 +65,8 @@ static struct callbacks_ {
struct cbslot tim7; struct cbslot tim7;
struct cbslot tim15; struct cbslot tim15;
struct cbslot adc1;
// XXX add more callbacks here when needed // XXX add more callbacks here when needed
} callbacks; } callbacks;
@ -96,7 +98,9 @@ void irqd_init(void)
HAL_NVIC_SetPriority(DMA1_Channel2_3_IRQn, 2, 0); HAL_NVIC_SetPriority(DMA1_Channel2_3_IRQn, 2, 0);
HAL_NVIC_SetPriority(DMA1_Channel4_5_6_7_IRQn, 2, 0); HAL_NVIC_SetPriority(DMA1_Channel4_5_6_7_IRQn, 2, 0);
// NVIC_EnableIRQ(ADC1_COMP_IRQn); /*!< ADC1 and COMP interrupts (ADC interrupt combined with EXTI Lines 21 and 22 */ NVIC_EnableIRQ(ADC1_COMP_IRQn); /*!< ADC1 and COMP interrupts (ADC interrupt combined with EXTI Lines 21 and 22 */
HAL_NVIC_SetPriority(ADC1_COMP_IRQn, 1, 0); // ADC group completion - higher prio than DMA to let it handle the last halfword first
// NVIC_EnableIRQ(TIM1_IRQn); /*!< TIM1 global Interrupt */ // NVIC_EnableIRQ(TIM1_IRQn); /*!< TIM1 global Interrupt */
// NVIC_EnableIRQ(TIM2_IRQn); /*!< TIM2 global Interrupt */ // NVIC_EnableIRQ(TIM2_IRQn); /*!< TIM2 global Interrupt */
// NVIC_EnableIRQ(TIM3_IRQn); /*!< TIM3 global Interrupt */ // NVIC_EnableIRQ(TIM3_IRQn); /*!< TIM3 global Interrupt */
@ -159,6 +163,8 @@ static struct cbslot *get_slot_for_periph(void *periph)
else if (periph == TIM7) slot = &callbacks.tim7; else if (periph == TIM7) slot = &callbacks.tim7;
else if (periph == TIM15) slot = &callbacks.tim15; else if (periph == TIM15) slot = &callbacks.tim15;
else if (periph == ADC1) slot = &callbacks.adc1;
else if (periph >= EXTIS[0] && periph <= EXTIS[15]) { else if (periph >= EXTIS[0] && periph <= EXTIS[15]) {
slot = &callbacks.exti[periph - EXTIS[0]]; slot = &callbacks.exti[periph - EXTIS[0]];
} }
@ -313,6 +319,11 @@ void TIM15_IRQHandler(void)
CALL_IRQ_HANDLER(callbacks.tim15); CALL_IRQ_HANDLER(callbacks.tim15);
} }
void ADC1_COMP_IRQHandler(void)
{
CALL_IRQ_HANDLER(callbacks.adc1);
}
// other ISRs... // other ISRs...

@ -18,6 +18,8 @@
void plat_init(void) void plat_init(void)
{ {
// GPIO clocks are enabled earlier in the GEX start-up hook
// Load system defaults // Load system defaults
systemsettings_init(); systemsettings_init();
@ -27,8 +29,9 @@ void plat_init(void)
LockJumper_Init(); LockJumper_Init();
Indicator_Init(); Indicator_Init();
DebugUart_Init(); // resource claim DebugUart_Init(); // resource claim (was inited earlier to allow debug outputs)
// Enable interrupts and set priorities
irqd_init(); irqd_init();
dbg("Loading settings ..."); dbg("Loading settings ...");

@ -2,3 +2,270 @@
// Created by MightyPork on 2018/02/04. // 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;
}

@ -2,6 +2,7 @@
// Created by MightyPork on 2018/02/03. // Created by MightyPork on 2018/02/03.
// //
#include <stm32f072xb.h>
#include "platform.h" #include "platform.h"
#include "unit_base.h" #include "unit_base.h"
@ -20,48 +21,12 @@ error_t UADC_preInit(Unit *unit)
priv->sample_time = 0b010; // 13.5c priv->sample_time = 0b010; // 13.5c
priv->frequency = 1000; priv->frequency = 1000;
priv->buffer_size = 512; priv->buffer_size = 512;
priv->enable_averaging = false;
priv->averaging_factor = 500;
return E_SUCCESS; priv->opmode = ADC_OPMODE_UNINIT;
}
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)) { return E_SUCCESS;
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 */ /** Finalize unit set-up */
@ -133,6 +98,7 @@ error_t UADC_init(Unit *unit)
// enable peripherals clock // enable peripherals clock
hw_periph_clock_enable(priv->ADCx); hw_periph_clock_enable(priv->ADCx);
hw_periph_clock_enable(priv->TIMx); hw_periph_clock_enable(priv->TIMx);
// DMA and GPIO clocks are enabled on startup automatically
} }
// ------------------- CONFIGURE THE TIMER -------------------------- // ------------------- CONFIGURE THE TIMER --------------------------
@ -142,8 +108,7 @@ error_t UADC_init(Unit *unit)
uint16_t presc; uint16_t presc;
uint32_t count; uint32_t count;
float real_freq; float real_freq;
if (!solve_timer(PLAT_APB1_HZ, priv->frequency, true, &presc, &count, if (!solve_timer(PLAT_APB1_HZ, priv->frequency, true, &presc, &count, &real_freq)) {
&real_freq)) {
dbg("Failed to resolve timer params."); dbg("Failed to resolve timer params.");
return E_BAD_VALUE; return E_BAD_VALUE;
} }
@ -167,24 +132,29 @@ error_t UADC_init(Unit *unit)
while (LL_ADC_IsCalibrationOnGoing(priv->ADCx)) {} while (LL_ADC_IsCalibrationOnGoing(priv->ADCx)) {}
dbg("ADC calibrated."); dbg("ADC calibrated.");
{
uint32_t mask = 0; uint32_t mask = 0;
if (priv->enable_vref) mask |= LL_ADC_PATH_INTERNAL_VREFINT; if (priv->enable_vref) mask |= LL_ADC_PATH_INTERNAL_VREFINT;
if (priv->enable_tsense) mask |= LL_ADC_PATH_INTERNAL_TEMPSENSOR; if (priv->enable_tsense) mask |= LL_ADC_PATH_INTERNAL_TEMPSENSOR;
LL_ADC_SetCommonPathInternalCh(priv->ADCx_Common, mask); LL_ADC_SetCommonPathInternalCh(priv->ADCx_Common, mask);
}
LL_ADC_SetDataAlignment(priv->ADCx, LL_ADC_DATA_ALIGN_RIGHT); LL_ADC_SetDataAlignment(priv->ADCx, LL_ADC_DATA_ALIGN_RIGHT);
LL_ADC_SetResolution(priv->ADCx, LL_ADC_RESOLUTION_12B); LL_ADC_SetResolution(priv->ADCx, LL_ADC_RESOLUTION_12B);
LL_ADC_REG_SetDMATransfer(priv->ADCx, LL_ADC_REG_DMA_TRANSFER_UNLIMITED); LL_ADC_REG_SetDMATransfer(priv->ADCx, LL_ADC_REG_DMA_TRANSFER_UNLIMITED);
// configure channels // configure channels
LL_ADC_REG_SetSequencerChannels(priv->ADCx, priv->channels); priv->extended_channels_mask = priv->channels;
if (priv->enable_tsense) LL_ADC_REG_SetSequencerChAdd(priv->ADCx, LL_ADC_CHANNEL_TEMPSENSOR); if (priv->enable_tsense) priv->extended_channels_mask |= (1<<16);
if (priv->enable_vref) LL_ADC_REG_SetSequencerChAdd(priv->ADCx, LL_ADC_CHANNEL_VREFINT); 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_REG_SetTriggerSource(priv->ADCx, LL_ADC_REG_TRIG_EXT_TIM15_TRGO);
LL_ADC_SetSamplingTimeCommonChannels(priv->ADCx, LL_ADC_SAMPLETIMES[priv->sample_time]); LL_ADC_SetSamplingTimeCommonChannels(priv->ADCx, LL_ADC_SAMPLETIMES[priv->sample_time]);
LL_ADC_Enable(priv->ADCx); // LL_ADC_Enable(priv->ADCx);
} }
// --------------------- CONFIGURE DMA ------------------------------- // --------------------- 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 // 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))); 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; if (itemcount % 2 == 1) itemcount -= priv->nb_channels;
priv->dma_buffer_size = (uint16_t) (itemcount * 2); priv->dma_buffer_itemcount = itemcount;
dbg("DMA item count is %d (%d bytes)", itemcount, 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_size); priv->dma_buffer = malloc_ck(priv->dma_buffer_itemcount * sizeof(uint16_t));
if (NULL == priv->dma_buffer) return E_OUT_OF_MEM; if (NULL == priv->dma_buffer) return E_OUT_OF_MEM;
assert_param(((uint32_t) priv->dma_buffer & 3) == 0); // must be aligned 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)); 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 // 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. // 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_HT(priv->DMAx, priv->dma_chnum);
LL_DMA_EnableIT_TC(priv->DMAx, priv->dma_chnum); // LL_DMA_EnableIT_TC(priv->DMAx, priv->dma_chnum);
} }
LL_DMA_EnableChannel(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;
dbg("ADC peripherals configured.");
irqd_attach(priv->DMA_CHx, UADC_DMA_Handler, unit);
irqd_attach(priv->ADCx, UADC_ADC_EOS_Handler, unit);
// FIXME - temporary demo - counter start... UADC_SwitchMode(unit, ADC_OPMODE_IDLE);
LL_ADC_REG_StartConversion(priv->ADCx); // the first conversion must be started manually
LL_TIM_EnableCounter(priv->TIMx);
return E_SUCCESS; return E_SUCCESS;
} }
@ -246,11 +224,15 @@ void UADC_deInit(Unit *unit)
// de-init peripherals // de-init peripherals
if (unit->status == E_SUCCESS ) { if (unit->status == E_SUCCESS ) {
UADC_SwitchMode(unit, ADC_OPMODE_UNINIT);
//LL_ADC_DeInit(priv->ADCx); //LL_ADC_DeInit(priv->ADCx);
LL_ADC_CommonDeInit(priv->ADCx_Common); LL_ADC_CommonDeInit(priv->ADCx_Common);
LL_TIM_DeInit(priv->TIMx); LL_TIM_DeInit(priv->TIMx);
irqd_detach(priv->DMA_CHx, UADC_DMA_Handler); irqd_detach(priv->DMA_CHx, UADC_DMA_Handler);
irqd_detach(priv->ADCx, UADC_ADC_EOS_Handler);
LL_DMA_DeInit(priv->DMAx, priv->dma_chnum); LL_DMA_DeInit(priv->DMAx, priv->dma_chnum);
free_ck(priv->dma_buffer); free_ck(priv->dma_buffer);

@ -11,6 +11,15 @@
#include "unit_base.h" #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 */ /** Private data structure */
struct priv { struct priv {
// settings // settings
@ -21,26 +30,41 @@ struct priv {
uint32_t frequency; //!< Timer frequency in Hz. Note: not all frequencies can be achieved accurately 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 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?) bool enable_averaging; //!< Enable exponential averaging
// TODO threshold watchdog with hysteresis (maybe a separate component?) uint16_t averaging_factor; //!< Exponential averaging factor 0-1000
// TODO trigger level, edge direction, hold-off, pre-trigger buffer (extract from the DMA buffer)
// internal state // internal state
ADC_TypeDef *ADCx; uint32_t extended_channels_mask; //!< channels bitfield including tsense and vref
ADC_Common_TypeDef *ADCx_Common; float avg_factor_as_float;
TIM_TypeDef *TIMx; ADC_TypeDef *ADCx; //!< The ADC peripheral used
DMA_TypeDef *DMAx; ADC_Common_TypeDef *ADCx_Common; //!< The ADC common control block
uint8_t dma_chnum; TIM_TypeDef *TIMx; //!< ADC timing timer instance
DMA_Channel_TypeDef *DMA_CHx; DMA_TypeDef *DMAx; //!< DMA isnatnce used
uint16_t *dma_buffer; uint8_t dma_chnum; //!< DMA channel number
uint8_t nb_channels; // nr of enabled adc channels DMA_Channel_TypeDef *DMA_CHx; //!< DMA channel instance
uint16_t dma_buffer_size; // real number of bytes 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)
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)
}; };
// 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
/** Allocate data structure and set defaults */ /** Allocate data structure and set defaults */
error_t UADC_preInit(Unit *unit); error_t UADC_preInit(Unit *unit);
@ -66,4 +90,21 @@ error_t UADC_init(Unit *unit);
/** Tear down the unit */ /** Tear down the unit */
void UADC_deInit(Unit *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 #endif //GEX_F072_ADC_INTERNAL_H

@ -86,12 +86,13 @@ void UADC_writeIni(Unit *unit, IniWriter *iw)
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_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_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_entry(iw, "enable_vref", str_yn(priv->enable_tsense));
iw_cmt_newline(iw);
iw_comment(iw, "Sampling time (0-7)"); iw_comment(iw, "Sampling time (0-7)");
iw_entry(iw, "sample_time", "%d", (int)priv->sample_time); 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, "- the buffer is shared by all channels");
iw_comment(iw, "- insufficient buffer size can lead to data loss"); iw_comment(iw, "- insufficient buffer size can lead to data loss");
iw_entry(iw, "buffer_size", "%d", (int)priv->buffer_size); 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, .deInit = UADC_deInit,
// Function // Function
.handleRequest = UADC_handleRequest, .handleRequest = UADC_handleRequest,
.updateTick = UADC_updateTick,
}; };

@ -5,6 +5,11 @@
void *malloc_ck_do(size_t size, const char *file, uint32_t line) void *malloc_ck_do(size_t size, const char *file, uint32_t line)
{ {
if (size == 0) {
_warn_msg(file, line, "MALLOC OF SIZE 0");
return NULL;
}
void *mem = pvPortMalloc(size); void *mem = pvPortMalloc(size);
_malloc_trace(size, mem, file, line); _malloc_trace(size, mem, file, line);
if (mem == NULL) { if (mem == NULL) {
@ -16,6 +21,7 @@ void *malloc_ck_do(size_t size, const char *file, uint32_t line)
void *calloc_ck_do(size_t nmemb, size_t size, const char *file, uint32_t line) void *calloc_ck_do(size_t nmemb, size_t size, const char *file, uint32_t line)
{ {
void *mem = malloc_ck_do(nmemb*size, file, line); void *mem = malloc_ck_do(nmemb*size, file, line);
if (mem == NULL) return NULL;
memset(mem, 0, size*nmemb); memset(mem, 0, size*nmemb);
return mem; return mem;
} }

Loading…
Cancel
Save