AnsweredAssumed Answered

STM32L4 Continous ADC DMA Multi-Channel Data Processing

Question asked by botha.antonie on Jan 31, 2018
Latest reply on Feb 1, 2018 by botha.antonie

Good Day


I have an ADC Configured for Continous conversion with DMA. I Periodically stop it and quickly process the data before starting it again. 


I would like to know how to process the data if I enable more than one channel, as the data is then interleaved in the  ADC DMA Array "getADCBuffer[2000]" - how can I split this into a separate array for each channel in the most efficient way? To use a 'for' loop and copy every nth byte into another array takes too much processing time and my ADC task can't keep up with what it should be measuring. 


Is it possible to set up a Memory to Memory DMA Stream and increment the source address by the number of channels while only incrementing the destination address by one? 


I am using an STM32L433 which only has one ADC. 

Some Code to illustrate what I am currently doing while only having the 1 channel configured:


ADC Config: 

void MX_ADC1_Init(void)
  ADC_ChannelConfTypeDef sConfig;

    /**Common config

  hadc1.Instance = ADC1;
  hadc1.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1;
  hadc1.Init.Resolution = ADC_RESOLUTION_12B;
  hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
  hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV;
  hadc1.Init.LowPowerAutoWait = DISABLE;
  hadc1.Init.ContinuousConvMode = ENABLE;
  hadc1.Init.NbrOfConversion = 1;
  hadc1.Init.DiscontinuousConvMode = DISABLE;
  hadc1.Init.NbrOfDiscConversion = 1;
  hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
  hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
  hadc1.Init.DMAContinuousRequests = ENABLE;
  hadc1.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
  hadc1.Init.OversamplingMode = DISABLE;
  if (HAL_ADC_Init(&hadc1) != HAL_OK)
    _Error_Handler(__FILE__, __LINE__);

    /**Configure Regular Channel

  sConfig.Channel = ADC_CHANNEL_4;
  sConfig.Rank = ADC_REGULAR_RANK_1;
  sConfig.SamplingTime = ADC_SAMPLETIME_2CYCLES_5;
  sConfig.SingleDiff = ADC_SINGLE_ENDED;
  sConfig.OffsetNumber = ADC_OFFSET_NONE;
  sConfig.Offset = 0;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
    _Error_Handler(__FILE__, __LINE__);

ADC DMA Config:
/* ADC1 DMA Init */
    /* ADC1 Init */
    hdma_adc1.Instance = DMA1_Channel1;
    hdma_adc1.Init.Request = DMA_REQUEST_0;
    hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
    hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
    hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
    hdma_adc1.Init.Mode = DMA_CIRCULAR;
    hdma_adc1.Init.Priority = DMA_PRIORITY_HIGH;
    if (HAL_DMA_Init(&hdma_adc1) != HAL_OK)
      _Error_Handler(__FILE__, __LINE__);


//I then start the ADC:
HAL_ADC_Start_DMA(&hadc1, (uint16_t*)getADCBuffer, adcBufferLength); \\ADC BuffLength = 2000

//And stop it when I want to process the data, giving a Binary Semaphore to a high priority processing task:
               else if (ICEventCount == 6)

                    uxSavedInterruptStatus = taskENTER_CRITICAL_FROM_ISR();
                    xSemaphoreGiveFromISR(processADC_BinSemHandle, &xHigherPriorityTaskWoken);
                    if(xHigherPriorityTaskWoken == pdTRUE)
                    taskEXIT_CRITICAL_FROM_ISR( uxSavedInterruptStatus ); // Done with the critical section
//I am only interested in the Maximum and Minimum value withing the array for this particular channel, I use the arm DSP Lib to get these:
if(xSemaphoreTake(processADC_BinSemHandle, portMAX_DELAY))
          dmaWritePtr = (adcBufferLength - (uint16_t)(DMA1_Channel1->CNDTR));
          dataCounter = dmaWritePtr - dmaReadPtr;

          if (dataCounter == 0)
          else if (dataCounter > 0)
               adcSampleLength = (uint32_t)dataCounter;
               arm_max_q15(getADCBuffer, adcSampleLength, &adcMaxSample, &sampleMaxIndex);
               arm_min_q15(getADCBuffer, adcSampleLength, &adcMinSample, &sampleMinIndex);
               adcSampleLength = (uint32_t)(adcBufferLength + dataCounter);
               arm_max_q15(getADCBuffer, adcSampleLength, &adcMaxSample, &sampleMaxIndex);
               arm_min_q15(getADCBuffer, adcSampleLength, &adcMinSample, &sampleMinIndex);
          ADC_HighWaterMark = uxTaskGetStackHighWaterMark( NULL );