cancel
Showing results for 
Search instead for 
Did you mean: 

TIM triggered ADC DMA issue

luke2
Associate II
Posted on May 13, 2016 at 10:53

Hi all,

I am using an STM32F303 to sample PA7 via ADC2 when TIM8 update triggers it, and using DMA to put the samples in a circular buffer. Although I seem to have gotten things basically working in the code below, I am having an issue once the DMA buffer is filled... I seem to be getting regular samples updating in the buffer with each TIM8_UP interrupt, but this stops once the buffer is filled and does not cycle back to the start of the buffer as would be expected from the circular mode on the DMA.

Any ideas what I am doing wrong?

uint16_t adc[8];

void init_adc() {

ADC_CommonInitTypeDef ADC_CommonInitStructure;

ADC_InitTypeDef ADC_InitStructure;

    GPIO_InitTypeDef GPIO_InitStructure;

    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;

    // Activate clocks

    RCC_ADCCLKConfig(RCC_ADC12PLLCLK_Div2);

RCC_AHBPeriphClockCmd(RCC_AHBPeriph_ADC12, ENABLE);

RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE);

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM8, ENABLE);

// Timer

    TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);

    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;

    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;

    TIM_TimeBaseStructure.TIM_Period = 1999;

    TIM_TimeBaseStructure.TIM_Prescaler = 8000;

    TIM_TimeBaseInit(TIM8, &TIM_TimeBaseStructure);

    TIM_SelectOutputTrigger(TIM8,TIM_TRGOSource_Update);

//TIM_SelectMasterSlaveMode(TIM8, TIM_MasterSlaveMode_Enable);

    TIM_ITConfig(TIM8, TIM_IT_Update, ENABLE);

    NVIC_InitTypeDef NVIC_InitStructure;

    NVIC_InitStructure.NVIC_IRQChannel = TIM8_UP_IRQn;

    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x0F;

    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x0F;

    NVIC_Init(&NVIC_InitStructure);

// Analog input PA7 -> ADC2_IN4

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 

    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;

    GPIO_Init(GPIOA, &GPIO_InitStructure);

    // Capture DMA

    DMA_InitTypeDef DMA_InitStructure;

     DMA_DeInit(DMA2_Channel1);

     DMA_StructInit(&DMA_InitStructure);

     DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)(adc);

     DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&(ADC2->DR));

     DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;

     DMA_InitStructure.DMA_BufferSize = 8;

     DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;

     DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;

     DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;

     DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;

     DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;

     DMA_InitStructure.DMA_Priority = DMA_Priority_High;

     DMA_Init(DMA2_Channel1, &DMA_InitStructure);

     DMA_Cmd(DMA2_Channel1, ENABLE);

     // Init struct

     ADC_StructInit(&ADC_InitStructure);

     ADC_CommonStructInit(&ADC_CommonInitStructure);

     ADC_Cmd(ADC2, DISABLE);

ADC_DeInit(ADC2);

// Initialize the ADC

ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;

ADC_CommonInitStructure.ADC_Clock = ADC_Clock_SynClkModeDiv2;//ADC_Clock_AsynClkMode;//

ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;//ADC_DMAAccessMode_1;//

ADC_CommonInitStructure.ADC_TwoSamplingDelay = 0;//0x05;// ADC_TwoSamplingDelay_5Cycles;

ADC_CommonInit(ADC2, &ADC_CommonInitStructure);

// ADC2 init

ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;

ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;//ENABLE;//

//ADC_InitStructure.ADC_ScanConvMode = ENABLE;

ADC_InitStructure.ADC_ExternalTrigConvEvent = ADC_ExternalTrigConvEvent_7;//TIM8_TRGO

ADC_InitStructure.ADC_ExternalTrigEventEdge = ADC_ExternalTrigEventEdge_RisingEdge;//BothEdge;//None;//

ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;

ADC_InitStructure.ADC_OverrunMode = DISABLE;//ENABLE;//

ADC_InitStructure.ADC_AutoInjMode = DISABLE;//ENABLE;//

ADC_InitStructure.ADC_NbrOfRegChannel = 1;

ADC_Init(ADC2, &ADC_InitStructure);

// Configure channel (ADC2_IN4 <- PA7)

ADC_RegularChannelConfig(ADC2, ADC_Channel_4, 1, ADC_SampleTime_19Cycles5);//1Cycles5);

ADC_DMACmd(ADC2, ENABLE);

ADC_Cmd(ADC2, ENABLE);

TIM_Cmd(TIM8, ENABLE);

ADC_StartConversion(ADC2);

}

void show_adc() {

//uint32_t w = (uint32_t)adc[0];

USART_word((uint32_t)adc[0]);

USART_word((uint32_t)adc[1]);

USART_word((uint32_t)adc[2]);

USART_word((uint32_t)adc[3]);

USART_word((uint32_t)adc[4]);

USART_word((uint32_t)adc[5]);

USART_word((uint32_t)adc[6]);

USART_word((uint32_t)adc[7]);

//USART_word(ADC2->DR);

}

void TIM8_UP_IRQHandler() {

        TIM_ClearITPendingBit(TIM8, TIM_IT_Update);

        show_adc();

}
4 REPLIES 4
mark239955_stm1
Associate II
Posted on May 13, 2016 at 11:31

I'd start by using debug and single step to check that the relevant bit flag (CIRC or similar, presumably) in the DMA control register is actually set as per your initialization struct when you issue the DMA_Init() call.

luke2
Associate II
Posted on May 13, 2016 at 18:12

The CIRC flag is set appropriately during a debug run. I don't get it, the TIM8 interrupt continues to trigger after the buffer is full but the memory is not being updated...

I must be missing something, probably really obvious? Or perhaps its some ADC idiosyncrasy, as I'm not very familiar with it (this is my first ADC project). 

Looking a bit deeper it has to be an issue with my ADC config, as the ADC->DR value gets frozen on the last value copied by the DMA. It seems like its doing a full buffer of samples and then stops taking samples, even though the trigger timer is still running.

The other odd thing that might explain this is the fact I seem to need to add

ADC_StartConversion(ADC2);

to get the thing to take any samples at all. When I do it takes a buffer of samples then stops as described. I thought this method was for software triggered sampling only? Does it need to be called to activate triggering? What am I missing here?

luke2
Associate II
Posted on May 13, 2016 at 18:56

Ok I have got it working now... there seems to be a bug in ADC_CommonInit() which is not setting the value of ADC2->CFGR correctly. When this code is run:

ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;

ADC_CommonInitStructure.ADC_Clock = ADC_Clock_SynClkModeDiv2;

ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_1;

ADC_CommonInitStructure.ADC_DMAMode = ADC_DMAMode_Circular;

ADC_CommonInitStructure.ADC_TwoSamplingDelay = 0;

ADC_CommonInit(ADC2, &ADC_CommonInitStructure);

it is supposed to configure the CFGR.DMACFG=1 to indicate circular DMA mode on the ADC, however I noted during debug that it was 0 even though I clearly set it to circular mode above. The problem can be fixed with the simple direct update to the flag:

ADC2->CFGR |= 0x00000002;

Looking at the ADC_CommonInit() I'm confused by why they have used the code they did, since they have added this to the flag disjunction formula:

... | (uint32_t)(ADC_CommonInitStruct->ADC_DMAMode << 12) | ...

This seems odd they would do this, since the shift should not have been used to get the proper flag to be set in bit position 2. Perhaps they ported code from another line that uses different flag positions and forgot to change it?

Walid FTITI_O
Senior II
Posted on May 16, 2016 at 17:05

Hi Luke,

In the last vesion V1.2.3 of

http://www.st.com/content/st_com/en/products/embedded-software/mcus-embedded-software/stm32-embedded-software/stm32-standard-peripheral-libraries/stsw-stm32html

, in the ADC driver, the functionADC_CommonInit() is the following:

tmpreg1 |= (uint32_t)(ADC_CommonInitStruct->ADC_Mode | 
ADC_CommonInitStruct->ADC_Clock | 
ADC_CommonInitStruct->ADC_DMAAccessMode | 
(uint32_t)(ADC_CommonInitStruct->ADC_DMAMode << 
12
) | 

(uint32_t)((uint32_t)ADC_CommonInitStruct >ADC_TwoSamplingDelay << 
8
));

if((ADCx == ADC1) || (ADCx == ADC2))
{ 
/* Write to ADC CCR */
ADC1_2->CCR = tmpreg1;
}
else
{
/* Write to ADC CCR */
ADC3_4->CCR = tmpreg1;
}

So it should set the bit 13 of the ADC2->CCR register but it is setting the bit I will report it internally to be corrected. Thanks for the feedbacks and contribution. -Hannibal-