STM32F103CBADC自效准有bug吗?

三生石   2010-9-16 22:52 楼主
用32F103CB AD测量6路电池电压,范围1-28V电压只通过电阻分压直接输入AD管脚,采用6路循环DMA自动采集方式,通过数字滤波基本可以到2mV内的跳动,现在碰到一个奇怪的现象:进行多次上电,偶尔数据会有一个恒定的测量偏移,大概8mV左右,这个恒定偏移只要不重新上电会一直在,显示的测量数据也稳定。比如:大多数的时候测量为3.123V, 偶尔一次上电为3.130V,可以肯定的是测试环境相同:我用万用表量过两种状态下VCC和电阻分压处电压是一致的。为了找原因我直接查看AD值,也同样有这个问题,可以排除中间数据处理问题。经过多次判断,问题锁定在AD开启前的AD自效准上(见红色代码),理由1是在程序中加入人为触发的AD自效准,不重新上电也会产生上面问题。理由2注释掉红色代码运行,反复上电不会有上面问题。
什么原因会导致AD自效准会有如此偏差,或自效准时外围电路要注意什么?我在网上搜不到有类似问题的内容,特来求大家给我指点迷津!救救我呀
void AD_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
ADC_InitTypeDef ADC_InitStructure;
DMA_InitTypeDef DMA_InitStructure;
/* Enable DMA clock             */
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

/* Enable ADC1 and GPIOA clocks */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA, ENABLE);
/* Configure (ADC Channel) as analog input -------------------------*/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_A_D | GPIO_Pin_A_V | GPIO_Pin_A_BAT1 | GPIO_Pin_A_BAT2 | \
         GPIO_Pin_A_BAT3 | GPIO_Pin_A_BAT4 | GPIO_Pin_A_BAT5 | GPIO_Pin_A_BAT6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStructure);

/* DMA1 channel1 configuration ----------------------------------------------*/
DMA_DeInit(DMA1_Channel1);
DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&ADCConvertedValue;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = RE_SAMPLE_COUNT*CH_MAX;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//DMA_MemoryInc_Disable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
//DMA_ITConfig(DMA1_Channel1,DMA1_IT_TC1,ENABLE); //开传输完中断
/* Enable DMA1 channel1 */
DMA_Cmd(DMA1_Channel1, ENABLE);
  
/* ADC1 configuration ------------------------------------------------------*/
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//ADC_Mode_RegSimult;
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = CH_MAX;
ADC_Init(ADC1, &ADC_InitStructure);

/* ADC1 regular channel2 configuration */  
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, CH_CURRENT+1, ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_2, CH_VOLT+1, ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_3, CH_BAT1+1, ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_4, CH_BAT2+1, ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_5, CH_BAT3+1, ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_6, CH_BAT4+1, ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_7, CH_BAT5+1, ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_8, CH_BAT6+1, ADC_SampleTime_55Cycles5);
// ADC1 regular channel16 Temp Sensor configuration
ADC_RegularChannelConfig(ADC1, ADC_Channel_16, CH_TEMP+1, ADC_SampleTime_55Cycles5);
#ifdef INT_REFV
// ADC1 regular channel17
ADC_RegularChannelConfig(ADC1, ADC_Channel_17, CH_IREFV+1, ADC_SampleTime_55Cycles5);
#endif
// Enable the temperature sensor and vref internal channel
ADC_TempSensorVrefintCmd(ENABLE);

/* Enable ADC1 DMA */
ADC_DMACmd(ADC1, ENABLE);

/* Enable ADC1 */
ADC_Cmd(ADC1, ENABLE);

/* Enable ADC1 reset calibaration register */   
ADC_ResetCalibration(ADC1);
/* Check the end of ADC1 reset calibration register */
while(ADC_GetResetCalibrationStatus(ADC1));

/* Start ADC1 calibaration */
ADC_StartCalibration(ADC1);
/* Check the end of ADC1 calibration */
while(ADC_GetCalibrationStatus(ADC1));

  
/* Start ADC1 Software Conversion */
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}

回复评论 (5)

目前还没有听说在ADC校准部分有Bug。

ADC校准是为了补偿环境的变化而设置的,包括温度和电压等变化,如果在校准和实际测量时这些环境参数不一致,就会出现测量误差。基于这个原理分析,会不会在你校准时的供电电压与随后进行实际测量时的电压不一样?你能不能检查一下?

另外有个问题:你反复上电测试的频率是多少?掉电多长时间后再上电?
点赞  2010-9-16 23:43
                                 谢谢版主答复。ADC自效准后,读出效准值,不同启动确实存在1个字的不同,对于我28V测量范围 28/4095=7mV左右的偏差,符合问题表现。目前我的解决是根据每次的ADC自效准值进行补偿修正。实质上是弃用了stm32内部的ADC效准
点赞  2010-9-17 10:22
LZ说的是个有意思的情况。
一个想法供参考:把ADC校准的代码,提前到对ADC的各个通道进行初始化前面,或许更好。
点赞  2010-9-17 11:13
                                 楼上的方法曾经也试过,跟放在初始化后边是一样的,还有曾经想过是不是vcc没稳定造成ADC效准偏差,结果吧ADC初始化移到n秒后运行还是一样。现在想想ADC自效准1个字相差也正常,但我8倍的过采样后放大了变化,才使问题凸显。
点赞  2010-9-17 12:45
楼上的方法曾经也试过,跟放在初始化后边是一样的,还有曾经想过是不是vcc没稳定造成ADC效准偏差,结果吧ADC初始化移到n秒后运行还是一样。现在想想ADC自效准1个字相差也正常,但我8倍的过采样后放大了变化,才使问 ...
赞同,ADC自效准只有1个LSb的差别,确实属于正常情况。
点赞  2010-9-17 13:59
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复