一、运行环境
- stm32f070
- STM32F0xx_StdPeriph_Lib
二、多采样通道切换
在之前的项目中,涉及有多个采样通道的采样,一直采用adc + dma的方式。后来因为需求的问题,要改成每次只采样一个通道。但是却发现,无论切换哪个通道,读取回来的参数都是一样的,明显跟实际不符合。
库函数提供的切换通道的函数
ADC_ChannelConfig ( ADC1, ADC_Channel_11, ADC_SampleTime_239_5Cycles )
里面的实现有一点与我所理解的不一样:
ADCx->CHSELR |= (uint32_t)ADC_Channel;
这里用的是 或 而不是赋值符号。所以每次切换通道的时候,上一次的通道其实还在采样。
我的修改方法:
ADC_ChannelConfig ( ADC1, ADC_Channel_11, ADC_SampleTime_239_5Cycles )
ADC1->CHSELR = ADC_Channel_11
三、采样精度问题
3. 1 一些基础概念
- VDDA: 模拟供电引脚(外部输入)
- VSSA: 模拟地
- Vrefint:内部参考电压。Vrefint 在单片机内部直接接到ADC_IN17,通过采样该通道的电压,可以计算出Vrefint。
最小值 | 典型值 | 最大值 |
---|---|---|
1.2v | 1.23v | 1.25v |
3.2 遇到的问题
adc 采样回来的电压跟实际电压偏差比较大。
问题分析:
- 采样时间短。
- 单片机供电电压不是稳定的3.3v。单片机虽然有模拟供电引脚,但是为了省成本,模拟供电引脚接了VDD。
解决办法:
采样时间的问题
一开始的采样周期选择的是ADC_SampleTime_13_5Cycles
,但是误差一直比较大,选择最大的采样周期(ADC_SampleTime_239_5Cycles
)后,误差才达标。
针对供电电压不稳的问题
-
使用标准电压芯片作为VDD,VDDA。
-
或者想办法计算出真正的模拟输入电压,也就是VDDA。
2. 1 官方提供的计算VDDA 的公式
2.2 其中VREFINT_CAL 是每个芯片出厂的时候,厂家校正好的参数,存放在flash中的某个只读区域。针对我这款单片机,存放地址是0x1FFF F7BA- 0x1FFF F7BB 长度为两个byte
2.3 VREFINT_DATA 是从ADC_CHN17 读取到的内部参考电压adc值。
2.4 我现在测试使用的单片机的实测数据:VDDA = 3.3v * 1524 /1615 ,即是3.144v。用万用表测到的vdd参数是3.150v。精确到0.01v。
2.5 示例代码
#define BSP_ADC_RD_VREFINT_CAL() (*(__IO uint16_t *)(0x1FFFF7BA)) //VREF_CAL 值
uint16_t bsp_adc_read_vrefint_data(void)
{
ADC_ChannelConfig ( ADC1, ADC_Channel_Vrefint, ADC_SampleTime_239_5Cycles );
ADC1->CHSELR = ADC_Channel_Vrefint;
ADC_VrefintCmd ( ENABLE );
ADC_StartOfConversion ( ADC1 );
while ( ADC_GetFlagStatus ( ADC1, ADC_FLAG_EOC ) == RESET )
;
return ADC_GetConversionValue ( ADC1 );
}
uint16_t bsp_adc_calc_vdda(void)
{
return (uint16_t)(3.3 * BSP_ADC_RD_VREFINT_CAL() / bsp_adc_read_vrefint_data());
}