cancel
Showing results for 
Search instead for 
Did you mean: 

DMA and ADC not in sync

mcu_user
Associate II
Posted on June 16, 2008 at 06:41

DMA and ADC not in sync

6 REPLIES 6
mcu_user
Associate II
Posted on May 17, 2011 at 12:35

Hi

I’ve run into a peculiar problem when using the DMA to continuously transfer my ADC values to RAM.

With certain ADC frequencies the DMA and ADC does not seem to be ''in sync'', and the values are stores in the wrong places.

Code snippet:

// Address defines:

#define ADC1_DR_Address ((u32)0x4001244C)

// Private variables

volatile struct

{

vu16 ADC_Value1;

vu16 ADC_Value2;

vu16 ADC_Value3;

vu16 ADC_Value4;

vu16 ADC_Value5;

vu16 ADC_Value6;

vu16 ADC_Value7;

vu16 ADC_Value8;

} ADCvalues;

void HardwareSetup(void)

{

(...)

DMA_InitTypeDef DMA_InitStructure;

ADC_InitTypeDef ADC1_InitStructure;

(...)

// System clock settings

RCC_DeInit();

RCC_HSEConfig(RCC_HSE_ON);

while (RCC_WaitForHSEStartUp()==ERROR)

{

}

RCC_PLLConfig(RCC_PLLSource_HSE_Div1,RCC_PLLMul_12); //Select the PLL clock source to be XTAL frequency x 12 = 48MHz

RCC_PLLCmd(ENABLE);

/* Wait till PLL is ready */

while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)

{

}

RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); //System clock is PLL clock

while(RCC_GetSYSCLKSource() != 0x08)

{

}

RCC_HCLKConfig(RCC_SYSCLK_Div1); //Base clock for all komponents (AHB) is 48MHz

RCC_PCLK1Config(RCC_HCLK_Div4); //Base clock for APB1 peripherals is 12MHz (Timer 2,3 and 4 clock is 24 MHz)

RCC_PCLK2Config(RCC_HCLK_Div4); //Base clock for APB2 peripherals is 12MHz (Timer 1 clock is 24 MHz)

(...)

RCC_ADCCLKConfig(RCC_PCLK2_Div2); //ADC clock is 2MHz // NOTE1!

RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA,ENABLE);//Enable DMA clock based on AHB

RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE); //Enables peripherals clock for ADC1

(...)

GPIO_InitStruct.GPIO_Pin = GPIO_Pin_2|GPIO_Pin_3; //Value5, Value6

GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;

GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AIN;

GPIO_Init(GPIOA,&GPIO_InitStruct);

// The other AIN pins are initialized in the same way

(...)

// DMA Channel1 setup

DMA_Cmd(DMA_Channel1, DISABLE);

DMA_DeInit(DMA_Channel1);

DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address; // Base address of ADC Data Register

DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&ADCvalues; // Base address of result table

DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; // Direction from PERIPH to MEM

DMA_InitStructure.DMA_BufferSize = 8; // Transfer eight ((half)words (8 x 16 bit))

DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // Don't increment address in peripheral

DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // Increment mem after each reception

DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; // DataSize in peripheral = 16 bit

DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; // DataSize in memory = 16 bit

DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; // Wrap around memory after eight receptions

DMA_InitStructure.DMA_Priority = DMA_Priority_High; // High priority DMA

DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; // No mem to mem transfer

DMA_Init(DMA_Channel1, &DMA_InitStructure);

// Enable DMA channel1

DMA_Cmd(DMA_Channel1, ENABLE);

// ADC1 setup:

ADC_Cmd(ADC1, DISABLE);

ADC_DeInit(ADC1);

ADC1_InitStructure.ADC_Mode = ADC_Mode_Independent; // Each ADC interface works independantly

ADC1_InitStructure.ADC_ScanConvMode = ENABLE; // Scan whole group of input pins

ADC1_InitStructure.ADC_ContinuousConvMode = ENABLE; // Continuously convert channels

ADC1_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; // Conversion initiated by SW only

ADC1_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; // Right align result

ADC1_InitStructure.ADC_NbrOfChannel = 8; // Convert eight channels

ADC_Init(ADC1, &ADC1_InitStructure);

// ADC1 regular channel configurations

ADC_RegularChannelConfig(ADC1, ADC_Channel_12, 1, ADC_SampleTime_55Cycles5); // Value1

ADC_RegularChannelConfig(ADC1, ADC_Channel_13, 2, ADC_SampleTime_55Cycles5); // Value2

ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 3, ADC_SampleTime_55Cycles5); // Value3

ADC_RegularChannelConfig(ADC1, ADC_Channel_9, 4, ADC_SampleTime_55Cycles5); // Value4

ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 5, ADC_SampleTime_55Cycles5); // Value5

ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 6, ADC_SampleTime_55Cycles5); // Value6

ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 7, ADC_SampleTime_55Cycles5); // Value7

ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 8, ADC_SampleTime_55Cycles5); // Value8

// Enable ADC1 DMA

ADC_DMACmd(ADC1, ENABLE);

// Enable ADC1

ADC_Cmd(ADC1, ENABLE);

// Enable ADC1 reset calibration register

ADC_ResetCalibration(ADC1);

// Check the end of ADC1 reset calibration register

while(ADC_GetResetCalibrationStatus(ADC1));

// Start ADC1 calibration

ADC_StartCalibration(ADC1);

// Check the end of ADC1 calibration

while(ADC_GetCalibrationStatus(ADC1));

// Start ADC1 Software Conversion // NOTE2

ADC_SoftwareStartConvCmd(ADC1, ENABLE);

// Check the end of the first DMA transfer

while(!DMA_GetFlagStatus(DMA_FLAG_TC1));

// Expecting the result of the first eight conversions to be ready

}

With the code shown above everything works fine, but if I change the ADC prescaler (“NOTE1�) to e.g. 2 or 4 (i.e. change the ADC frequency to 6 MHz or 3 MHz, the values in the struct are moved either one or two places. The value of what I expect to be the first AD conversion (Value1), is now stored in either ADCValues.ADC_Value2 or in ADCvalues.ADC_Value3, the value of the second conversion (Value2) is stored in ADCvalues.ADC_Value3 or ADCvalues.ADC_Value4 and so forth.

When changing the ADC Prescaler back to 6 (ADC frequency = 2 MHz) or even 8 (ADC frequency = 1.5 MHz) the values are stored where I expect them to be stored, i.e. Value1 in ADCvalues.ADC_Value1 and so on.

Another interesting aspect is that if I add a delay (e.g. 1 ms) or break at “NOTE2� in the code above - that is before actually starting the conversion - and then continue, everything works fine regardless of the ADC frequency.

What am I not seeing here? It seems the DMA stores a value or two before the ADC has performed its first conversion, but if that is the case I don’t understand why the problem only materializes when speeding up the ADC clock. Shouldn’t it be the other way around?

Might this be because of the time that the ADC needs to start converting accurately, and if so, how does one calculate this. And, why does the DMA get ''out of sync''?

[ This message was edited by: mcu_user1 on 14-05-2008 11:01 ]

paulsmitton9
Associate II
Posted on May 17, 2011 at 12:35

I think I've something similar. I'd no idea what was causing it, but was guessing a spurious triggering of the DMA perhaps, so I put:

Code:

DMA_Cmd(DMA_Channel1, ENABLE);

ADC_SoftwareStartConvCmd(ADC1, ENABLE);

(there doesn't seem any need to enable the dma any sooner).

Have you got any further with your investigation?

smart
Associate II
Posted on May 17, 2011 at 12:35

I am running DMA and ADC conversion for all 16 channels with ADCLK 18 MHZ and 1.5 sampling rate. It seems to be working fine.

You should use the DMA interrupt in place ot putting any delay to get the data in your destination table.

mcu_user
Associate II
Posted on May 17, 2011 at 12:35

@paul.smitton:

I agree. There seems to be no reason to enable the DMA any sooner than you do, but sadly it did not help my project. The values are still ''out of sync''.

@smart.avr:

What interrupt are you referring to? As far as I can
kdcsh
Associate
Posted on May 17, 2011 at 12:35

are you overcome the probleme?

mcu_user
Associate II
Posted on May 17, 2011 at 12:35

Well yes, in a way I have, but I can't say if this is the optimal solution.

What I ended up with - after some trial and error - was rearranging the initialization, enabling and startup of the two peripherals, making the order like this:

(...)

// ADC1 Setup

ADC_Cmd(ADC1, DISABLE);

ADC_DeInit(ADC1);

ADC1_InitStructure.ADC_Mode = ADC_Mode_Independent; // Each ADC interface works independantly

ADC1_InitStructure.ADC_ScanConvMode = ENABLE; // Scan whole group of input pins

ADC1_InitStructure.ADC_ContinuousConvMode = ENABLE; // Continuously convert channels

ADC1_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; // Conversion initiated by SW only

ADC1_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; // Right align result

ADC1_InitStructure.ADC_NbrOfChannel = 8; // Convert eight channels

ADC_Init(ADC1, &ADC1_InitStructure);

// ADC1 regular channel configurations

ADC_RegularChannelConfig(ADC1, ADC_Channel_12, 1, ADC_SampleTime_55Cycles5); // Value1

ADC_RegularChannelConfig(ADC1, ADC_Channel_13, 2, ADC_SampleTime_55Cycles5); // Value2

ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 3, ADC_SampleTime_55Cycles5); // Value3

ADC_RegularChannelConfig(ADC1, ADC_Channel_9, 4, ADC_SampleTime_55Cycles5); // Value4

ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 5, ADC_SampleTime_55Cycles5); // Value5

ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 6, ADC_SampleTime_55Cycles5); // Value6

ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 7, ADC_SampleTime_55Cycles5); // Value7

ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 8, ADC_SampleTime_55Cycles5); // Value8

// Enable ADC1

ADC_Cmd(ADC1, ENABLE);

// Enable ADC1 reset calibration register

ADC_ResetCalibration(ADC1);

// Check the end of ADC1 reset calibration register

while(ADC_GetResetCalibrationStatus(ADC1));

// Start ADC1 calibration

ADC_StartCalibration(ADC1);

// Check the end of ADC1 calibration

while(ADC_GetCalibrationStatus(ADC1));

// DMA Channel1 setup

DMA_Cmd(DMA_Channel1, DISABLE);

DMA_DeInit(DMA_Channel1);

DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address; // Base address of ADC Data Register

DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&ADCvalues; // Base address of result table

DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; // Direction from PERIPH to MEM

DMA_InitStructure.DMA_BufferSize = 8; // Transfer eight ((half)words (8 x 16 bit))

DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // Don't increment address in peripheral

DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // Increment mem after each reception

DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; // DataSize in peripheral = 16 bit

DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; // DataSize in memory = 16 bit

DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; // Wrap around memory after eight receptions

DMA_InitStructure.DMA_Priority = DMA_Priority_High; // High priority DMA

DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; // No mem to mem transfer

DMA_Init(DMA_Channel1, &DMA_InitStructure);

// Enable DMA channel1

DMA_Cmd(DMA_Channel1, ENABLE);

// Enable ADC1 DMA

ADC_DMACmd(ADC1, ENABLE);

// Start ADC1 Software Conversion

ADC_SoftwareStartConvCmd(ADC1, ENABLE);

// Check the end of the first DMA transfer

while(!DMA_GetFlagStatus(DMA_FLAG_TC1));

// The result of the first eight conversions is ready

// End ADC1 + DMA1

(...)

In short:

- I first set up the ADC, enable it and calibrate it, but I do NOT enable DMA on the ADC yet.

- Then I set up the DMA and enable it.

- Finally I enable DMA on ADC and start the conversion.

I am still not sure what caused the problem in the first place (maybe the calibration of the ADC trigged the DMA transfer?), but after making these changes I seem able to run the ADC at all speeds.

[ This message was edited by: mcu_user1 on 16-06-2008 10:13 ]