AnsweredAssumed Answered

Problem with reading ADC channels group using DMA

Question asked by BeginEnd on Sep 30, 2013
Latest reply on Oct 1, 2013 by Clive One
I am using STM32F407VG.
I've read documentation about ADC and groups and still don't know how to set up properly ADC to sample periodically fixed ADC channels. I've done this by following code but don't know if this is right. I want to use ADC2, sample group of channels 10, 11, 12, 13 and 0 in this order. Samples tranfered using DMA. Currently using TIM2 and update event to trigger sample conversion.

DMA config:
static const DMA_InitTypeDef analog_dma_stream_conf =
{
  .DMA_Channel = DMA_Channel_1,
  .DMA_PeripheralBaseAddr = (uint32_t)&ANALOG_ADC->DR,
  .DMA_Memory0BaseAddr = (uint32_t)analog_dma_buffers[0],
  .DMA_BufferSize = sizeof(analog_dma_buffers[0]),
 
  .DMA_DIR = DMA_DIR_PeripheralToMemory,
 
  .DMA_MemoryInc = DMA_MemoryInc_Enable,
  .DMA_PeripheralInc = DMA_PeripheralInc_Disable,
 
  .DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord,
  .DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord,
 
  .DMA_Priority = DMA_Priority_High,
  .DMA_Mode = DMA_Mode_Normal,
 
  .DMA_FIFOMode = DMA_FIFOMode_Disable,
  .DMA_FIFOThreshold = DMA_FIFOThreshold_1QuarterFull,
 
  .DMA_MemoryBurst = DMA_MemoryBurst_Single,
  .DMA_PeripheralBurst = DMA_PeripheralBurst_Single,
};
 
DMA_Init(DMA2_Stream3, &analog_dma_stream_conf);
DMA_ITConfig(DMA2_Stream3, DMA_IT_TC, ENABLE);
DMA_Cmd(DMA2_Stream3, ENABLE);
 
void ANALOG_DMA_Stream_IRQHandler( void )
{
  DMA_ClearITPendingBit(DMA2_Stream3, DMA_IT_TCIFx);
 
  struct analog_sample_vector *next_buf = NULL, *cur_mem;
  signed portBASE_TYPE res = xQueueReceiveFromISR(analog_dma_free_queue, &next_buf, NULL);
 
  if( res == pdTRUE )
  {
    cur_mem = (struct analog_sample_vector *)ANALOG_DMA_Stream->M0AR;
    DMA2_Stream3->M0AR = (uint32_t)next_buf;
    DMA2_Stream3->NDTR = ANALOG_BUFFER_SIZE * sizeof(struct analog_sample_vector);
    DMA2_Stream3->CR |= DMA_SxCR_EN;
 
    signed portBASE_TYPE woken;
    res = xQueueSendToBackFromISR(analog_dma_ready_queue, &cur_mem, &woken);
    assert( res == pdTRUE );
 
    analog_thread_ready_waiting |= woken;
    portYIELD_FROM_ISR( analog_thread_ready_waiting && xQueueIsQueueEmptyFromISR(analog_dma_free_queue) );
  }
  else if( analog_thread_ready_waiting )
    portYIELD();
}


static const ADC_InitTypeDef adc2_conf =
{
        .ADC_Resolution = ADC_Resolution_12b,
        .ADC_ScanConvMode = DISABLE,
        .ADC_ContinuousConvMode = DISABLE,
        .ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising,
        .ADC_ExternalTrigConv = ANALOG_ADC_ExternalTrigConv,
        .ADC_DataAlign = ADC_DataAlign_Right,
        .ADC_NbrOfConversion = 5,
};
 
RCC_APB2PeriphClockCmd(RCC_APB2ENR_ANALOG_ADCEN, ENABLE);
ADC_Init(ANALOG_ADC, &adc2_conf);
 
ADC_RegularChannelConfig(ADC2, ADC_Channel_10, 1, ADC_SampleTime_480Cycles);
ADC_RegularChannelConfig(ADC2, ADC_Channel_11, 2, ADC_SampleTime_480Cycles);
ADC_RegularChannelConfig(ADC2, ADC_Channel_12, 3, ADC_SampleTime_480Cycles);
ADC_RegularChannelConfig(ADC2, ADC_Channel_13, 4, ADC_SampleTime_480Cycles);
ADC_RegularChannelConfig(ADC2, ADC_Channel_0, 5, ADC_SampleTime_480Cycles);
 
ADC_DMACmd(ADC2, ENABLE);

Then the trigger config

TIM_TimeBaseInitTypeDef timer_conf;
timer_conf.TIM_Prescaler = presc ? presc - 1 : 0;
timer_conf.TIM_CounterMode = TIM_CounterMode_Up;
timer_conf.TIM_Period = period - 1;
timer_conf.TIM_ClockDivision = TIM_CKD_DIV1;
timer_conf.TIM_RepetitionCounter = 0;
 
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_TimeBaseInit(TIM2, &timer_conf);
TIM_ARRPreloadConfig(TIM2, ENABLE);
TIM_SelectMasterSlaveMode(TIM2, TIM_MasterSlaveMode_Enable);
TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update);
TIM_Cmd(TIM2, ENABLE);

The problem is I am getting only one DMA interrupt. It seams that ADC stops requesting DMA transfers, but looked at register it seems all is set ok.

Does anyone have any hints or code snippet that uses ADC channels group periodically sampled and and is using DMA. Maby better datasheet?

Outcomes