STM32F0 中 ADC 多通道转换结果相同的问题

前言

前段时间调试 STM32F030 的 ADC,在多通道转换时遇到了奇怪的问题,使用官方的例程和库函数连续转换多个 ADC 通道,得到的几个通道的结果是一样的,解决办法参考了 关于STM32F0系列多路ADC单独采样数据相同问题的处理,在此表示感谢。

记录

在官方库的例程 ADC_BasicExample 中的初始化和转换方法如下

ADC_InitTypeDef     ADC_InitStructure;
GPIO_InitTypeDef    GPIO_InitStructure;

/* GPIOC Periph clock enable */
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOC, ENABLE);

/* ADC1 Periph clock enable */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);

/* Configure ADC Channel11 as analog input */
#ifdef USE_STM320518_EVAL
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 ;
#else
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 ;
#endif /* USE_STM320518_EVAL */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;
GPIO_Init(GPIOC, &GPIO_InitStructure);
  
/* ADCs DeInit */  
ADC_DeInit(ADC1);

/* Initialize ADC structure */
ADC_StructInit(&ADC_InitStructure);

/* Configure the ADC1 in continuous mode with a resolution equal to 12 bits  */
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; 
ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_ScanDirection = ADC_ScanDirection_Upward;
ADC_Init(ADC1, &ADC_InitStructure); 

/* Convert the ADC1 Channel 11 with 239.5 Cycles as sampling time */ 
#ifdef USE_STM320518_EVAL
ADC_ChannelConfig(ADC1, ADC_Channel_11 , ADC_SampleTime_239_5Cycles);
#else
ADC_ChannelConfig(ADC1, ADC_Channel_10 , ADC_SampleTime_239_5Cycles);
#endif /* USE_STM320518_EVAL */

/* ADC Calibration */
ADC_GetCalibrationFactor(ADC1);

/* Enable the ADC peripheral */
ADC_Cmd(ADC1, ENABLE);     

/* Wait the ADRDY flag */
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_ADRDY)); 

/* ADC1 regular Software Start Conv */ 
ADC_StartOfConversion(ADC1);

while (1)
{
    /* Test EOC flag */
    while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);
    
    /* Get ADC1 converted data */
    ADC1ConvertedValue =ADC_GetConversionValue(ADC1);
    
    /* Compute the voltage */
    ADC1ConvertedVoltage = (ADC1ConvertedValue *3300)/0xFFF;
    
    /* Display converted data on the LCD */
    Display();
}

可见库函数中切换通道是使用 ADC_ChannelConfig(ADC1, ADC_Channel_11, ADC_SampleTime_239_5Cycles),在库函数中 ADC_ChannelConfig 函数源码如下

void ADC_ChannelConfig(ADC_TypeDef* ADCx, uint32_t ADC_Channel, uint32_t ADC_SampleTime)
{
    uint32_t tmpreg = 0;

    /* Check the parameters */
    assert_param(IS_ADC_ALL_PERIPH(ADCx));
    assert_param(IS_ADC_CHANNEL(ADC_Channel));
    assert_param(IS_ADC_SAMPLE_TIME(ADC_SampleTime));

    /* Configure the ADC Channel */
    ADCx->CHSELR |= (uint32_t)ADC_Channel;

    /* Clear the Sampling time Selection bits */
    tmpreg &= ~ADC_SMPR1_SMPR;

    /* Set the ADC Sampling Time register */
    tmpreg |= (uint32_t)ADC_SampleTime;

    /* Configure the ADC Sample time register */
    ADCx->SMPR = tmpreg ;
}

其中 ADCx->CHSELR |= (uint32_t)ADC_Channel 对寄存器赋值使用了“|”,这会造成下一次循环转换时仍然转换最高位通道
解决办法是,在每次调用 ADC_ChannelConfig 之后,再对寄存器写入

uint16_t ADC_Get_Result(uint32_t ch)
{
    ADC_ChannelConfig(ADC1, ch, ADC_SampleTime_239_5Cycles);
    ADC1->CHSELR = ch;
    while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_ADRDY)){};
    ADC_StartOfConversion(ADC1);
    while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET){};
    return ADC_GetConversionValue(ADC1)&0x00000fff;
}
posted @ 2018-10-17 09:12  HintLee  阅读(4772)  评论(0编辑  收藏  举报