adc EOS IRQ speedup with a lookup array, achievable up to 100kHz with short capture window

remotes/github/master
Ondřej Hruška 6 years ago
parent c0846f0bb7
commit 02b0ceb139
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 105
      units/adc/_adc_core.c
  2. 1
      units/adc/_adc_init.c
  3. 2
      units/adc/_adc_internal.h
  4. 1
      units/adc/unit_adc.c

@ -327,30 +327,41 @@ void UADC_DMA_Handler(void *arg)
*/
void UADC_ADC_EOS_Handler(void *arg)
{
// GPIOC->BSRR = 0x01;
Unit *unit = arg;
struct priv *priv = unit->data;
const bool can_average = priv->cfg.enable_averaging &&
priv->real_frequency_int < UADC_MAX_FREQ_FOR_AVERAGING;
// Normally
uint64_t timestamp = 0;
if (priv->opmode == ADC_OPMODE_ARMED) timestamp = PTIM_GetMicrotime();
if (priv->opmode == ADC_OPMODE_ARMED) {
timestamp = PTIM_GetMicrotime();
}
LL_ADC_ClearFlag_EOS(priv->ADCx);
if (priv->opmode == ADC_OPMODE_UNINIT) {
goto exit;
}
// GPIOC->BSRR = 0x02;
// Wait for the DMA to complete copying the last sample
uint32_t dmapos = DMA_POS(priv);
// Wait for the DMA to complete copying the last sample
// XXX
// experiments revealed this was actually a bug somewhere else and DMA
// is quick enough so we don't have to worry about this
#if 0
uint32_t err = (dmapos % priv->nb_channels);
if (err != 0) {
GPIOC->BSRR = 0x02;
hw_wait_while(((dmapos = DMA_POS(priv)) % priv->nb_channels) != 0, 10);
GPIOC->BRR = 0x02;
}
// GPIOC->BRR = 0x02;
#endif
// wrap dmapos to be past the last sample, even if outside the buffer
// - so we can subtract nb_channels
uint32_t sample_pos;
if (dmapos == 0) {
sample_pos = (uint32_t) (priv->buf_itemcount);
@ -359,21 +370,26 @@ void UADC_ADC_EOS_Handler(void *arg)
}
sample_pos -= priv->nb_channels;
uint8_t cnt = 0; // index of the sample within the group
const bool can_average = priv->cfg.enable_averaging && priv->real_frequency_int < UADC_MAX_FREQ_FOR_AVERAGING;
const uint32_t channels_mask = priv->channels_mask;
uint16_t val;
// TODO change this to a pre-computed byte array traversal
#if 1
for (uint32_t j = 0; j < priv->nb_channels; j++) {
const uint8_t i = priv->channel_nums[j];
val = priv->dma_buffer[sample_pos+j];
if (can_average) {
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;
}
#else
for (uint8_t i = 0; i < 18; i++) {
if (channels_mask & (1 << i)) {
// if (cnt == priv->nb_channels-1) {
// val = last_sample; // DMA may not have finished copying it yet
// } else {
val = priv->dma_buffer[sample_pos+cnt];
// }
val = priv->dma_buffer[sample_pos+cnt];
cnt++;
@ -386,35 +402,44 @@ void UADC_ADC_EOS_Handler(void *arg)
priv->last_samples[i] = val;
}
}
#endif
switch (priv->opmode) {
// Triggering condition test
case ADC_OPMODE_ARMED:
val = priv->last_samples[priv->trigger_source];
if ((priv->trig_prev_level < priv->trig_level) &&
val >= priv->trig_level &&
(bool) (priv->trig_edge & 0b01)) {
// Rising edge
UADC_HandleTrigger(unit, 0b01, timestamp);
}
else if ((priv->trig_prev_level > priv->trig_level) &&
val <= priv->trig_level &&
(bool) (priv->trig_edge & 0b10)) {
// Falling edge
UADC_HandleTrigger(unit, 0b10, timestamp);
}
priv->trig_prev_level = val;
break;
// auto-rearm was waiting for the next sample
case ADC_OPMODE_REARM_PENDING:
if (!priv->auto_rearm) {
// It looks like the flag was cleared by DISARM before we got a new sample.
// Let's just switch to IDLE
UADC_SwitchMode(unit, ADC_OPMODE_IDLE);
} else {
// Re-arming for a new trigger
UADC_SwitchMode(unit, ADC_OPMODE_ARMED);
}
// Triggering condition test
if (priv->opmode == ADC_OPMODE_ARMED) {
val = priv->last_samples[priv->trigger_source];
if ((priv->trig_prev_level < priv->trig_level) && val >= priv->trig_level && (bool) (priv->trig_edge & 0b01)) {
// Rising edge
UADC_HandleTrigger(unit, 0b01, timestamp);
}
else if ((priv->trig_prev_level > priv->trig_level) && val <= priv->trig_level && (bool) (priv->trig_edge & 0b10)) {
// Falling edge
UADC_HandleTrigger(unit, 0b10, timestamp);
}
priv->trig_prev_level = val;
}
// auto-rearm was waiting for the next sample
else if (priv->opmode == ADC_OPMODE_REARM_PENDING) {
if (!priv->auto_rearm) {
// It looks like the flag was cleared by DISARM before we got a new sample.
// Let's just switch to IDLE
UADC_SwitchMode(unit, ADC_OPMODE_IDLE);
} else {
// Re-arming for a new trigger
UADC_SwitchMode(unit, ADC_OPMODE_ARMED);
}
default:
break;
}
exit:
// GPIOC->BRR = 0x01;
return;
}

@ -122,6 +122,7 @@ error_t UADC_init(Unit *unit)
priv->nb_channels = 0;
for (uint8_t i = 0; i <= UADC_MAX_CHANNEL; i++) {
if (priv->cfg.channels & (1UL << i)) {
priv->channel_nums[priv->nb_channels] = (uint8_t) i;
priv->nb_channels++;
do {

@ -57,6 +57,8 @@ struct priv {
uint8_t dma_chnum; //!< DMA channel number
DMA_Channel_TypeDef *DMA_CHx; //!< DMA channel instance
uint8_t channel_nums[18];
// Live config
float real_frequency;
uint32_t real_frequency_int;

@ -140,6 +140,7 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P
// count the enabled channels
for(int i = 0; i < 32; i++) {
if (new_channels & (1<<i)) {
priv->channel_nums[nb_channels] = (uint8_t) i;
nb_channels++;
}
}

Loading…
Cancel
Save