ADC 电压采集
硬件原理
下图分别为 0-30V 和 0-3V 的电压采集电路
分压电阻选型
- 设计测量电压的最大值,出于安全考虑,本电路为 30V(实际最大可显示 99.9V 或 100V);
- ADC 参考电压,本电路中为 1.5V,该参考电压可以通过程序进行配置;
- 功耗,为了降 低采样电路的功耗,通常根据经验值将低侧电阻(R7)选择为 10K;
随后便可以通过以上参数计算出分压电阻的高侧电阻:
- 计算所需的分压比例:即 ADC 参考电压:设计输入电压,通过已知参数可以计算出 1.5V/30V=0.05
- 计算高侧电阻:即低侧电阻/分压比例,通过已知参数可以计算出 10K/0.05=200K
- 选择标准电阻:选择一颗略高于计算值的电阻,计算值为 200K,通常我们选择 E24 系列电阻,因此本电路中选择大于 200K 且最接近的 220K。
如果在实际使用中,需要测量的电压低于 2/3 的模块设计电压,即 66V,则可以根据实际情况更换分压电阻并修改程序从而提升测量的精度,下面将进行案例说明:
- 假设被测电压不高于 24V,其他参数不变
- 通过计算可以得到 1.5V/24V=0.0625,10K/0.0625=160K,160K 为标准 E24 电阻可以直接选用,或适当留出冗余量选择更高阻值的 180K
如果在实际使用中,需要测量的电压若高于模块 99V 的设计电压,可以选择更换分压电阻或通过修改基准电压来实现更大量程的电压测量范围,下面将进行案例说明:
- 假设被测电压为 160V,选择提升电压基准的方案扩大量程
- 已知选用电阻的分压比例为 0.0145,通过公式反推,我们可以计算出 160V*0.0145=2.32V,因此我们可以选择 2.5V 的电压基准来实现量程的提升(扩大量程将会降低精度)
考虑到被测电源可能存在波动,在电路设计时,在低侧分压电阻上并联了 10nF 的滤波电容提高测量稳定性。
常见设计要点
常见值落在测量量程中间
由于 ADC 的缘故,如果某个值经常出现,则应该把这个值设为中间值,如果参考电压是 3V3,那么 1V65 是一个较好的值。
分压电阻功耗
为了降低采样电路的功耗,通常根据经验值将低侧电阻(R7)选择为 10K;则上端电阻根据公式计算即可。
上端电阻大小选择
再根据公式计算出来阻值以后最好要往上一点,因为电压是波动的,这样做可以留有冗余。
电阻精度
由于是分压的场景,最后的电压是需要根据公式计算,则电阻精度越高越好,例如 1%
功耗和封装
务必确保有足够的功耗余量,不然电阻发热会导致实际值和测量值不一致。
封装和测量电压
在测量较高电压的时候需要考虑高压可能产生电弧的可能,如果用太小封装,如 0402 可能会打火烧坏 MCU。
MCU 钳位保护
二极管钳位保证 MCU 安全
这个设计额外在采样电路中增加了一个 1N4148(D1 等)作为钳位二极管。尽可能避免在学习和调试使用中由于接入不正确的电压,导致芯片引脚损坏。 二极管钳位是一种重要的电子电路设计技术,它的主要作用是通过限制电压的幅度来保护电路,避免信号过大或过小导致的损坏或故障。
钳位在电路中是指限制电压的意思,而二极管钳位特指利用二极管将电路中的某点电位进行限制的技术。
二极管钳位主要利用了二极管的单向导电性。当二极管的正极电压大于负极电压并且导通后,二极管两端的电压被限制在其管压降上,通常硅管的管压降约为 0.7V。
钳位过程:通过二极管的钳位作用,将被钳位的电位强制拉向参考端,从而实现电位的限制。钳位并不改变原信号的波形,只是抬高或降低了信号的基准电位。
根据二极管连接方式的不同,钳位电路可分为正向钳位电路和负向钳位电路。本电路仅设计了正向钳位。
正向钳位电路:当二极管的正极接地时,为正向钳位电路。在正半周时,二极管截止 ;在负半周时,二极管导通,电容被充电至一定电压,使输出电压限制在一定范围内。 负向钳位电路:当二极管的负极接地时,为负向钳位电路。工作原理与正向钳位电路相反。
参考电压
ADC 的参考电压是非常重要的,因为 AD 值是根据参考电压计算得来,所以如果在高精度场合是需要使例如 TL431 来作为参考电压。
测量最大值
在测量高电压的时候需要额外考虑问题,建议在上面这种方式下最高测量 30V,如果高于 30V 最好使用专门的测量芯片。
多路复用
是否需要多路复用的情况,如果需要可以使用某些电子开关器件去进行多路复用。
软件方面示例代码
均值滤波也称为线性滤波,其采用的主要方法为邻域平均法。线性滤波的基本原理是用均值代替原图像中的各个像素值,即对待处理的当前像素点(x,y),选择一个模板,该模板由其近邻的若干像素组成,求模板中所有像素的均值,再把该均值赋予当前像素点(x,y),作为处理后图像在该点上的灰度 g(x,y),即 g(x,y)=∑f(x,y)/m,m 为该模板中包含当前像素在内的像素总个数。
这本是数字图像处理的一种方法,但也可以用在我们数字电压电流表的 ADC 采样数据上。我们选取二十次的 ADC 采样值存储在数组 Volt_Buffer 中,然后去除掉数组中的最大值和最小值后再取平均,得到的值作为结果显示在数码管上,这样可以较大程度获得准确的、不易波动的数据。
程序在实验五的基础上略作修改即可,首先是增加和修改变量:
#define ADC_SAMPLE_SIZE (20) //选取20个值来计算均值
uint16_t Volt_Buffer[ADC_SAMPLE_SIZE]; //存储ADC采样值
uint16_t Cal_Buffer = 0; //计算电压时的中间变量
uint32_t Led_Dis_Time; //运行函数的时间计数,300ms运行一次
接下来是 ADC 的采样函数,该函数在 BTIM1 的定时器中断服务程序中执行,该中断服务程序 1ms 执行一次,函数运行效果为 1ms 获得一次 ADC 的采样原始值储存进 Volt_Buffer[ADC_SAMPLE_SIZE] 数组中。
void Get_ADC_Value(void)
{
static uint8_t adc_cnt; //定义静态局部变量
ADC_GetSqr0Result(&Volt_Buffer[adc_cnt]); //取得 ADC 的原始采样值
adc_cnt++;
if(adc_cnt >= ADC_SAMPLE_SIZE) //采样 20 次后从 0 开始继续
{
adc_cnt = 0;
}
}
void BTIM1_IRQHandler(void)
{
static uint32_t Cnt;
/* USER CODE BEGIN */
if (BTIM_GetITStatus(CW_BTIM1, BTIM_IT_OV))
{
BTIM_ClearITPendingBit(CW_BTIM1, BTIM_IT_OV);
Get_ADC_Value(); //运行采样函数
ADC_GetSqr0Result(&Volt_Buffer);
Cnt++;
if (Cnt > 2)
{
//3ms显示一个数码管
Cnt = 0;
Display_Refresh();//数码管扫描显示
}
}
/* USER CODE END */
}
接下来是均值滤波的主体函数:
uint32_t Mean_Value_Filter(uint16_t *value, uint32_t size) //均值滤波
{
uint32_t sum = 0;
uint16_t max = 0;
uint16_t min = 0xffff;
int i;
for(i = 0; i < size; i++) //遍历数组找到最大值和最小值
{
sum += value[i];
if(value[i] > max)
{
max = value[i];
}
if(value[i] < min)
{
min = value[i];
}
}
sum -= max + min; //减去最大和最小值后求平均
sum = sum / (size - 2);
return sum;
}
在主函数中重新计算采样和显示值,并且让该计算 300ms 运行一次,以防数码管频繁改变显示值使得人眼无法分辨其显示的数字。
int main()
{
RCC_Configuration(); //时钟初始化
Seg_Configuration(); //数码管初始化
Btim1_Configuration();//BTIM1初始化
ADC_Configuration(); //ADC初始化
while(1)
{
if(GetTick() >= (Led_Dis_Time + 300)) //300ms改变一次数码管显示值
{
Led_Dis_Time = GetTick();
Cal_Buffer = Mean_Value_Filter(Volt_Buffer,ADC_SAMPLE_SIZE);
Cal_Buffer = (Cal_Buffer * ADC_REF_VALUE >> 12) * (R2 + R1)/R1;
if(Cal_Buffer % 10 >= 5)Cal_Buffer = Cal_Buffer / 10 + 1; // 四舍五入
else Cal_Buffer = Cal_Buffer / 10; //此时的值为标准值的100倍
Dynamic_Display(Cal_Buffer);
}
}
}