2012-12-12 06:06 AM
I am trying to use the STM32F4 ADC1 in a simple polled loop. HCLK is 168MHz.
My code calls RCC_Configuration_Adc1, GPIO_Configuration_Adc1, ADC_DeInit and ADC1_Configuration then goes to the main loop. Stepping through the code in the debugger seems to show the ADC is configured and started but EOC is never set. If I comment out the wait on EOC then the ADC DR seems to update with results. I'm confused. Is it possible to use the ADC for single coversions with EOC polled or do I need to set up a run of regular conversions? I have browsed for examples but they're DMA which I'm already using for other things.void RCC_Configuration_Adc1(void)
{
//for ADC1 on PC0 using IN10
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
}
void GPIO_Configuration_Adc1(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_StructInit(&GPIO_InitStructure);
//for ADC1 on PC0 using IN10
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;
GPIO_Init(GPIOC, &GPIO_InitStructure);
}
ADC_DeInit(); //de-initialises ALL the ADCs in the microcontroller
void ADC1_Configuration(void)
{
ADC_CommonInitTypeDef ADC_CommonInitStructure;
ADC_InitTypeDef ADC_InitStructure;
//common init defaults are mode = independent, prescaler = 2, DMA disabled, two sampling delay = 5
ADC_CommonStructInit(&ADC_CommonInitStructure);
//ADCCLK is common to all ADCs and generated from the APB2 clock divided by a prescaler /2, /4, /6 or /8.
//APB2 is HCLK/2 = 168 / 2 = 84MHz and ADC clock must be less than 30MHz so prescale by 4
ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4;
ADC_CommonInit(&ADC_CommonInitStructure);
//adc init defaults are 12 bit, continuous = DISABLE, ExternalTrigConvEdge_None,
//ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1, Data aligned right, number of conversions = 1;
ADC_StructInit(&ADC_InitStructure);
ADC_Init(ADC1, &ADC_InitStructure);
ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_480Cycles);
ADC_EOCOnEachRegularChannelCmd(ADC1, ENABLE);
ADC_Cmd(ADC1, ENABLE); //The ADC is powered on by setting the ADON bit in the ADC_CR2 register.
//When the ADON bit is set for the first time, it wakes up the ADC from the Power-down mode.
}
//in main ...
while(1)
{
uint16_t ADCResult = 0x1234;
ADC_SoftwareStartConv(ADC1);
while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);
ADCResult = ADC_GetConversionValue(ADC1);
}
#adc-eoc
2012-12-12 07:31 PM
Enable scan conversion mode.
Cheers, Hal2012-12-13 12:41 AM
Hi Hal,
Thanks for the suggestion but that doesn't seem to fix it. The documentation for ScanConvMode says, ''Specifies whether the conversion is performed in Scan (multichannels) or Single (one channel) mode.''. I only want to convert in Single (one channel) mode and had assumed EOC would ''just work''. If I can't get this going, I'll try to set up a scan mode (maybe with DMA) but I'd still like to understand why I dont see EOC being set after one conversion started with ADC_SoftwareStartConv(ADC1).2012-12-13 03:40 AM
I think I understand this now. I am using Keil MicroVision and in the debugger had the View / System Viewer / ADC / ADC1 window open. This displays the ADC DR result (presumably by reading the register) as well as other registers. The EOC flag is cleared when DR is read so you can never see the EOC flag set and also it seems like stepping through the code will always fail if EOC is checked. By closing the View / System Viewer / ADC / ADC1 window everything works as expected and the code runs or single steps reliably.
By the way, the line,ADC_EOCOnEachRegularChannelCmd(ADC1, ENABLE);
IS required even when using the ADC in this simple way.
2012-12-22 11:10 AM
Hi John,
I got the same issue here, but the compiler I use is GCC. The very first time EOC reading is never set, but sometimes when I press the reset button repeatedly, the ADC works as normal until I reset the board again. The chance of getting EOC for the first time is like 10% by pressing reset button repeatedly. So, I think something is not right.Later on I tried to change the optimization to -O0 and everything seems fine. But, after I added some other program, the ADC stops working and again pressing reset button repeatedly fixed the issue. So, this is not about optimization level. The only thing can fix this issue is by changing randomly the optimization level or changing some lines in the program. This is very weird.Any solution for me? Do you think you can try to change your optimization level and see if your ADC keeps working?I cannot use DMA because motor control require me to sample the signal at the right timing. Thank you.2012-12-22 01:01 PM
I cannot use DMA because motor control require me to sample the signal at the right timing.
I'm not quite sure I understand why, DMA and TIM driven ADC sampling are extremely accurate and predictable. Probably less helpful for variable timing, but if you light off a conversion under interrupt you should be able to catch the EOC interrupt later.2012-12-22 07:59 PM
I followed exactly the given code above. I don't use timer nor interrupt to catch EOC. The first EOC never fires. I modified the code to work with interrupt, again the first interrupt never happens. It does the same with continuous conversion. The first conversion never fires an EOC, but the second one and the rest do.
I think something is missing here in the code. I read from other discussion thread and they suggested to put some delay after waking up the ADC before starting the conversion. I put the delay gradually increased up to 1 second and the issue is still there.2012-12-22 08:18 PM
The workaround I did is as seen below:
while(1)
{ uint16_t ADCResult = 0x1234; uint16_t timeout; timeout = 0; ADC_SoftwareStartConv(ADC1); while(ADC1, ADC_FLAG_EOC) == RESET) { timeout++; if (timeout == 0xFFFF) { printf(''ADC EOC timeout detected!\n''); break; } } ADCResult = ADC_GetConversionValue(ADC1);}I want to remove this workaround and make it right since I don't see any errata about ADC on STM32F4. ADCResult is 0x1234 for the first conversion and that is the way I detect the error.2012-12-23 07:57 AM
The problem might be confusion over the difference between structure init and register init. ADC_DeInit resets ADC peripheral registers to power on values, and John F. has tried to take advantage of that by defining only changes in the ADC init structures. But the structure is in program data RAM. Compiler optimization levels might be adding confusion by eliminating unused init code in the library.
Try initializing all structure members before calling the init functions, includingExternalTrigConv = ADC_ExternalTrigConv_T1_CC1
which is missing from older F4 library examples.
Cheers, Hal
2013-04-11 08:05 AM
Hello! I have same problem. And my solution is add some function call to initialization:
void ADC3_Once_Measurement_Config(void)
{
ADC_InitTypeDef ADC_InitStructure;
ADC_CommonInitTypeDef ADC_CommonInitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
ADC_StructInit(&ADC_InitStructure);
ADC_CommonStructInit(&ADC_CommonInitStructure);
GPIO_StructInit(&GPIO_InitStructure);
/* Enable peripheral clocks *************************************************/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC3, ENABLE);
/* Enable GPIO clocks ****************************************/
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2 | RCC_AHB1Periph_GPIOC, ENABLE);
/* Configure ADC3 Channel12 pin as analog input ******************************/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;
GPIO_Init(GPIOC, &GPIO_InitStructure);
/* ADC Deinitialization *****************************************************/
ADC_DeInit();
/* ADC Common Init **********************************************************/
ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div2;
ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_20Cycles;
ADC_CommonInit(&ADC_CommonInitStructure);
/* ADC3 Init ****************************************************************/
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfConversion = 1;
ADC_Init(ADC3, &ADC_InitStructure);
ADC_EOCOnEachRegularChannelCmd(ADC3, ENABLE);
ADC_ContinuousModeCmd(ADC3, DISABLE);
ADC_DiscModeChannelCountConfig(ADC3, 1);
ADC_DiscModeCmd(ADC3, ENABLE);
/* ADC3 regular channel12 configuration *************************************/
ADC_RegularChannelConfig(ADC3, ADC_Channel_12, 1, ADC_SampleTime_3Cycles);
/* Enable ADC1 **************************************************************/
ADC_Cmd(ADC3, ENABLE);
}
And call this function for measure:
uint16_t ADC3_Measure(void)
{
uint16_t Value = 0;
/* Start ADC3 Software Conversion */
ADC_SoftwareStartConv(ADC3);
/* Wait for ACD conversion complete */
while( ADC_GetFlagStatus(ADC3, ADC_FLAG_EOC) == RESET )
{
};
/* Read value */
Value = ADC_GetConversionValue(ADC3);
return Value;
}