2008-06-15 09:41 PM
DMA and ADC not in sync
2011-05-17 03:35 AM
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 ]2011-05-17 03:35 AM
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?2011-05-17 03:35 AM
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.2011-05-17 03:35 AM
@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 can2011-05-17 03:35 AM
are you overcome the probleme?
2011-05-17 03:35 AM
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 ]