目录
前言:本文的MCU采用STM32F103ZET6,使用CubeMX配置ADC初始化函数。本文实现了直接采样和使用众数滤波采样,通过对比两者的采样值,可以明显看到众数滤波对ADC的采样精度有很大的改善。
一 ADC简介
ADC,中文名称模数转换器。它可以将外部的模拟信号转化成数字信号。使用它去读取IO口上的数值将不再是简单的0或1,而是连续可变的数值。ADC采样就是把随时间连续变化的模拟量转换为时间离散的模拟量。
ADC几个比较重要的参数:
(1)测量范围:输入范围指的是ADC能够接受的模拟输入信号的电压范围。超出此范围的信号可能会被截断,导致失真。 |
(2)分辨率:分辨率是ADC输出的二进制位数,它表示ADC可以将输入模拟信号分成多少个离散的级别。假如 ADC 的测量范围为 0-5V,分辨率为12位,那么我们能测出来的最小电压就是 5V除以2 的 12 次方。更高的分辨率通常意味着更精确的采样,但也需要更多的处理能力。 |
(3)采样速率: 采样速率表示ADC在一秒内采集的样本数。它决定了ADC对模拟信号的采样频率。高采样速率通常有助于捕捉高频信号,但也可能导致更大的数据处理压力。 |
(4)参考电压: 参考电压是ADC用来比较和量化输入信号的基准电压。稳定的参考电压对于获得准确的ADC输出至关重要。 |
STM32F103ZET6 系列一般都有 3 个 ADC,这些 ADC 可以独立使用,也可以使用多模式(提高采样率)。 STM32F103ZET6的 ADC 是 12 位逐次逼近型的模拟数字转换器。
二 STM32CubeMX配置
2.1 Debug设置成Serial Wire
不设置可能导致芯片自锁。
2.2 开启高速外部时钟HSE
2.3 配置时钟源
2.4 配置ADC
选择ADC的通道1,对应的引脚为PA1。
2.5 配置串口,方便查看数据。
选择USART1,配置成异步模式,对应的TX为PA9,RX为PA10。
2.6 生成代码
三、程序编写
3.1 添加头文件
#include "stdio.h"
3.2 添加全局变量
float BAT=0;
int32_t AD_BAT=0;
uint16_t Most_data[4096]={0}; //放在全局变量防止堆栈段溢出
uint16_t k = 0; //出现次数最多的数
uint16_t ii=0;
uint16_t max=0;
3.3 串口重定向
int fputc(int ch, FILE *f) //串口重定向
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);
return ch;
}
3.4 ADC采样代码编写
3.4.1 直接采样,不添加滤波
代码如下:
//开启ADC校准
HAL_ADCEx_Calibration_Start(&hadc1);
//开启ADC转换
HAL_ADC_Start(&hadc1); //first conv
//等待转换完成
if(HAL_ADC_PollForConversion(&hadc1, 200)== HAL_OK)
{
//获取在当前供电条件下的ADC采样值
if(HAL_IS_BIT_SET(HAL_ADC_GetState(&hadc1), HAL_ADC_STATE_REG_EOC))
{
AD_BAT = HAL_ADC_GetValue(&hadc1);
}
}
printf("ADC_value = %d\r\n",AD_BAT);
HAL_ADC_Stop(&hadc1); //stop conv
得到的采样值如下图,可以看到采样值在较大的范围内波动。
3.4.2 添加均值滤波方法
选取10次采样值的平均值做为最后的采样值,代码如下。
void ADC_GetValue(void)
{
int32_t All_AD_BAT=0;
for(uint8_t i=10;i>0;i--)
{
//开启ADC校准
HAL_ADCEx_Calibration_Start(&hadc1);
//开启ADC转换
HAL_ADC_Start(&hadc1); //first conv
//等待转换完成
if(HAL_ADC_PollForConversion(&hadc1, 200)== HAL_OK)
{
//获取在当前供电条件下的ADC采样值
if(HAL_IS_BIT_SET(HAL_ADC_GetState(&hadc1), HAL_ADC_STATE_REG_EOC))
{
AD_BAT = HAL_ADC_GetValue(&hadc1);
}
}
HAL_ADC_Stop(&hadc1); //stop conv
HAL_Delay(1);
All_AD_BAT += AD_BAT;
}
AD_BAT=All_AD_BAT/10;
BAT=3.3*AD_BAT/4095.0; //位移传感器输出电压
printf("ADC_value = %d\r\n",AD_BAT);
}
使用均值滤波得到的电压值相较于没有滤波更加稳定,可以使得到的电压值更加平滑。
3.4.3 添加众数滤波方法
选取20次采样值中出现次数最多的做为最后的采样值,代码如下。
for(ii=0;ii<20;ii++)
{
//开启ADC校准
HAL_ADCEx_Calibration_Start(&hadc1);
//开启ADC转换
HAL_ADC_Start(&hadc1); //first conv
//等待转换完成
if(HAL_ADC_PollForConversion(&hadc1, 200)== HAL_OK)
{
//获取在当前供电条件下的ADC采样值
if(HAL_IS_BIT_SET(HAL_ADC_GetState(&hadc1), HAL_ADC_STATE_REG_EOC))
{
AD_BAT = HAL_ADC_GetValue(&hadc1);
}
}
HAL_ADC_Stop(&hadc1); //stop conv
HAL_Delay(10);
Most_data[AD_BAT]++; //读到的数据,其数组中对应的值+1
}
max = Most_data[0]; //查找数组中的最大数,并且最大数为出现的次数
for(ii=0;ii<4096;ii++)
{
if(max<Most_data[ii])
k = ii;
}
for(ii=0;ii<4096;ii++) //清空数组,防止堆栈段溢出
Most_data[ii]=0;
printf("ADC_value = %d\r\n",k);
得到的采样值如下图,可以看到采样值在很小的范围内波动。
通过对比,可以看到添加了众数滤波后采样准确度更高了,对小频率的电压异常有很好的效果。