2012-11-06 10:39 AM
Hi: I am using an an STM32F217, and have some difficulty understanding the behaviour of the ADC peripheral. I want to set up ADC1 on pin PA7 to convert on-demand i.e. would like to poll the ADC pin when I want to, otherwise leaving the ADC idle.
I initially tried following the example given in the STM32F2xx firmware library, however as soon as a conversion is started, the OVR flag is set, and that somehow causes the EOC flag to never be set, thus hanging my thread. Once I started deliberately clearing the OVR flag, I could get the conversions started. However, this time all the data seemed to be left-aligned, although I set it to right-alignment. Furthermore, in the firmware library example, the parameter ADC_ExternalTrigConv is not being set. When I assigned any acceptable value to this parameter e.g. ADC_ExternalTrigConv_T1_CC1, I managed to get right-aligned data in the DR. I still get OVR errors ( with ADC1_CR2_DMA = 0 and ADC1_CR2_ECOS = 1 ). Could someone kindly shine some light on what I could be doing wrong here? Here's my configuration code as it is right now, with proper data alignment but OVR being generated every time. Thanks for any comments.void adc1_init( void )
{ GPIO_InitTypeDef gpio; ADC_CommonInitTypeDef adc_common; ADC_InitTypeDef adc; // Enable GPIO clock RCC_AHB1PeriphClockCmd( RCC_AHB1Periph_GPIOA, ENABLE ); // // Configure PA7 as ADC input // gpio.GPIO_Pin = GPIO_Pin_7; gpio.GPIO_Mode = GPIO_Mode_AN; GPIO_Init( GPIOA, &gpio );// Enable ADC1 clock and reset ADC1 peripheral
RCC_APB2PeriphClockCmd( RCC_APB2Periph_ADC1, ENABLE ); RCC_APB2PeriphResetCmd( RCC_APB2Periph_ADC1, ENABLE ); RCC_APB2PeriphResetCmd( RCC_APB2Periph_ADC1, DISABLE ); // Configure ADC1 adc.ADC_Resolution = ADC_Resolution_12b; adc.ADC_ScanConvMode = DISABLE; adc.ADC_ContinuousConvMode = DISABLE; adc.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None; adc.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1; adc.ADC_DataAlign = ADC_DataAlign_Right; adc.ADC_NbrOfConversion = 1; ADC_Init( ADC1, &adc ); ADC_RegularChannelConfig( ADC1, ADC_Channel_7, 1, ADC_SampleTime_480Cycles ); ADC_EOCOnEachRegularChannelCmd( ADC1, ENABLE ); ADC_Cmd( ADC1, ENABLE ); return; }unsigned short read_adc1( void )
{ // ignore overrun // - ADC_SR->OVR gets set despite disabling DMA ADC_ClearFlag( ADC1, ADC_FLAG_OVR ); // start conversion ADC_SoftwareStartConv( ADC1 ); // wait until conversion complete while( ADC_GetFlagStatus( ADC1, ADC_FLAG_EOC ) == RESET ); // return measured value return ADC_GetConversionValue( ADC1 ); } #stm32-adc2012-11-06 11:46 AM
This seems to be performing without triggering OVR in my application.
#include <
stdio.h
>
#include <
stdlib.h
>
#include ''stm32f2xx.h''
//******************************************************************************
void USART3_Configuration(void);
//******************************************************************************
void ADC_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
ADC_InitTypeDef ADC_InitStructure;
ADC_CommonInitTypeDef ADC_CommonInitStructure;
/* Enable peripheral clocks */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
/* Configure ADC Channel 7 pin as analog input (ADC12_IN7) */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* ADC Common configuration *************************************************/
ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;
ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div2;
ADC_CommonInit(&ADC_CommonInitStructure);
/* ADC1 regular channel 7 configuration ************************************/
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 = 0; // ensure all fields initialized
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfConversion = 1;
ADC_Init(ADC1, &ADC_InitStructure);
/* ADC1 regular channel 7 configuration */
ADC_RegularChannelConfig(ADC1, ADC_Channel_7, 1, ADC_SampleTime_480Cycles);
/* Enable ADC1 **************************************************************/
ADC_Cmd(ADC1, ENABLE);
}
//******************************************************************************
int main(void)
{
/*!< At this stage the microcontroller clock setting is already configured,
this is done through SystemInit() function which is called from startup
file (startup_stm32f2xx.s) before to branch to application main.
To reconfigure the default setting of SystemInit() function, refer to
system_stm32f2xx.c file
*/
USART3_Configuration(); // Setup USART for debugging
ADC_Configuration();
puts(''ADC1 Channel 7'');
while(1)
{
uint32_t ADC1ConvertedVoltage;
uint16_t ADC1ConvertedValue;
volatile int i;
/* Start ADC1 Software Conversion */
ADC_SoftwareStartConv(ADC1);
/* Wait on EOC */
while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);
ADC1ConvertedValue = (ADC_GetConversionValue(ADC1) & 0x0FFF);
ADC1ConvertedVoltage = ADC1ConvertedValue * 2800 / 0x1000; // 2.8V reference
printf(''ADC1 PA7 %5d mV
'', ADC1ConvertedVoltage);
if (ADC_GetFlagStatus(ADC1, ADC_FLAG_OVR) != RESET)
printf(''Overflowing!'');
for(i=0; i<10000000; i++); // Some arbitrary delay for dramatic purposes...
}
while(1); /* Infinite loop */
}
//******************************************************************************
2012-11-06 01:50 PM
As always, thanks Clive. That seems to work fine for me too, although I still don't see why changing the ADC prescalar and not calling the ADC_EOCOnEachRegularChannelCmd( ADCx, NewState ) function made the difference. I had ADC3 also being used in another thread, and it was also failing along with ADC1. Making these changes in ADC1 configuration works well for now, thanks.
2012-12-22 11:20 AM
Sunny, I got the same issue with the misalignment, it turned out because of optimization level. Somehow, if I change the optimization level to -O3 or -Os, the DR alignment code is optimized out. I didn't dig into the library function, but I put the ADC init optimization always as -O0.
Do you think you can try your old (not working) code with optimization -O0 and see if it works for you. If it does, then that might be the answer why we had misalignment.2012-12-22 11:35 AM
What compiler/tool-chain are we talking about?
If you have compiler optimization issues pay particular attention to how local/automatic variables are being initialized, assume the stack contains random junk. If you want to provide a consistent set of conditions perhaps you could try memset()'ing the init structures to zero, or using = { 0 };2012-12-22 07:44 PM
Your solution to initialize all init structure seems to work as workaround at the moment. I still have no idea why at certain optimization level that alignment part is optimized out. Initializing ADC init structure to all zero coincide with putting the value of data alignment right to 0x0 from the first place. Anyway, I use GCC and I will leave that issue to the compiler folks. Thank you for pointing me to the neat solution.
2012-12-23 07:24 AM
Anyway, I use GCC and I will leave that issue to the compiler folks.
I'm not sure it's an issue they will deal with, with non-specific initialization you can/should expect to get random initial values in registers or the stack. Optimization just tends to make some intrinsic problems more evident. That's not to say there aren't sometimes bugs in code optimizers, but more often it is the lack of rigor in language usage/syntax.