在单片机中,常用的ADC采样滤波算法有以下几种:
1、平均值滤波:
平均值滤波是一种简单的数字信号处理技术,用于消除离群点(noise)和去除高频噪声。这种滤波方法的原理是在一定时间内对所采集到的数据进行求平均数处理,以去除数据中的噪音部分。
举个例子,对于一组采样数据如下:{5, 7, 9, 11, 8}。如果存在噪音(例如读数误差),那么可以通过平均值滤波来去除它。通过计算这组数据的平均值:(5+7+9+11+8)/5 = 8,可以得到滤波后的结果为8。
简单来说,平均值滤波就是将一定时间内所采集到的数据求平均值,用来代替原始数据,以达到去除噪音的目的。即使在信号中存在周期性噪声时,平均值滤波也可以起到一定的作用。
//平均值滤波的全局变量
#define SAMPLE_SIZE 16 //采样数
volatile uint16_t samples[SAMPLE_SIZE];//存储adc采样值
volatile uint16_t sample_count = 0;//采样次数记录
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) {
// 读取采样结果
samples[sample_count++] = HAL_ADC_GetValue(hadc);
if (sample_count == SAMPLE_SIZE) {
// 完成一组采样,进行滤波处理
uint32_t sum = 0;
for (int i = 0; i < SAMPLE_SIZE; i++) {
sum += samples[i];
}
uint16_t average = sum / SAMPLE_SIZE;
// 清空采样计数器,重新开始采样
sample_count = 0;
HAL_ADC_Start_IT(hadc);
}
}
2、简单滑动平均滤波
#include "stm32f4xx_hal.h"
#define ADC_BUF_SIZE 16 // ADC采样缓冲区大小
#define AVG_BUF_SIZE 4 // 平均滤波缓冲区大小
static ADC_HandleTypeDef hadc; // ADC句柄
static uint16_t adc_buf[ADC_BUF_SIZE]; // ADC采样缓冲区
static uint16_t adc_index = 0; // ADC采样缓冲区索引
static uint16_t adc_sum = 0; // ADC采样缓冲区累加和
static uint16_t avg_buf[AVG_BUF_SIZE]; // 平均滤波缓冲区
static uint16_t avg_index = 0; // 平均滤波缓冲区索引
static uint16_t avg_sum = 0; // 平均滤波缓冲区累加和
void ADC_Init(void)
{
ADC_ChannelConfTypeDef sConfig;
__HAL_RCC_ADC1_CLK_ENABLE();
hadc.Instance = ADC1;
hadc.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
hadc.Init.Resolution = ADC_RESOLUTION_12B;
hadc.Init.ScanConvMode = DISABLE;
hadc.Init.ContinuousConvMode = ENABLE;
hadc.Init.DiscontinuousConvMode = DISABLE;
hadc.Init.NbrOfDiscConversion = 0;
hadc.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc.Init.NbrOfConversion = 1;
hadc.Init.DMAContinuousRequests = DISABLE;
hadc.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
if (HAL_ADC_Init(&hadc) != HAL_OK)
{
Error_Handler();
}
sConfig.Channel = ADC_CHANNEL_0;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_480CYCLES;
sConfig.Offset = 0;
if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
{
Error_Handler();
}
HAL_ADC_Start_DMA(&hadc, (uint32_t*)adc_buf, ADC_BUF_SIZE); // 启动DMA采样
}
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
uint16_t adc_data;
adc_data = adc_buf[adc_index]; // 获取最新的ADC采样数据
adc_sum += adc_data; // 累加和
adc_index++; // 索引+1
if (adc_index >= ADC_BUF_SIZE)
{
adc_index = 0;
}
if (avg_sum == 0) // 如果平均滤波缓冲区尚未初始化
{
for (uint16_t i = 0; i < AVG_BUF_SIZE; i++)
{
avg_buf[i] = adc_data; // 初始化平均滤波缓冲区
avg_sum += adc_data;
}
}
else
{
avg_sum -= avg_buf[avg_index]; // 减去最旧的数据
avg_buf[avg_index] = adc_data; // 存储最新的数据
avg_sum += adc_data; // 加入最新的数据
avg_index++; // 索引+1
if (avg_index >= AVG_BUF_SIZE)
{
avg_index = 0;
}
}
}
uint16_t ADC_Average_Filter(void)
{
return (uint16_t)(avg_sum / AVG_BUF_SIZE);
}
在此示例中,我使用了DMA采样,并在每次采样完成后调用了HAL_ADC_ConvCpltCallback回调函数。在回调函数中,我获取最新的ADC采样数据,并将其累加到adc_sum变量中。如果平均滤波缓冲区尚未初始化,则将其初始化为最新的ADC采样值;否则,将最新的ADC采样值存储到平均滤波缓冲区中,并将最旧的数据从累加和中减去,将最新的数据加入累加和中。
最后,我提供了一个ADC_Average_Filter函数来计算平均滤波结果,该函数返回平均滤波缓冲区中所有数据的平均值。
3、中位值滤波
#include "stm32f4xx_hal.h"
#define SAMPLES 10 // 采样数据数量
#define FILTER_SIZE 5 // 滤波器大小
ADC_HandleTypeDef hadc;
uint16_t samples[SAMPLES]; // 保存采样数据的数组
uint16_t filteredValue; // 过滤后的数据
// 中值滤波器函数
uint16_t medianFilter(uint16_t *data, uint8_t size)
{
uint16_t tempVal;
// 冒泡排序
for (uint8_t i = 0; i < size - 1; i++)
{
for (uint8_t j = i + 1; j < size; j++)
{
if (data[j] < data[i])
{
tempVal = data[i];
data[i] = data[j];
data[j] = tempVal;
}
}
}
// 返回中间值
return data[size / 2];
}
int main(void)
{
HAL_Init();
// 初始化ADC
__HAL_RCC_ADC1_CLK_ENABLE();
hadc.Instance = ADC1;
hadc.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV2;
hadc.Init.Resolution = ADC_RESOLUTION_12B;
HAL_ADC_Init(&hadc);
// 配置ADC通道
ADC_ChannelConfTypeDef sConfig = {0};
sConfig.Channel = ADC_CHANNEL_0;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
HAL_ADC_ConfigChannel(&hadc, &sConfig);
// 连续采样
HAL_ADC_Start(&hadc);
// 采集多个样本
for (uint8_t i = 0; i < SAMPLES; i++)
{
HAL_ADC_PollForConversion(&hadc, 1000); // 等待采样完成
samples[i] = HAL_ADC_GetValue(&hadc); // 保存采样值
HAL_Delay(10); // 延迟10毫秒
}
// 进行中值滤波
filteredValue = medianFilter(samples, FILTER_SIZE);
while (1)
{
// 循环执行其他代码
}
}
此示例从ADC采样器中获取多个采样值,保存在数组中。然后,采用中值滤波算法,根据给定的滤波器大小,获取中间值,作为过滤后的数据。注意,在示例中,使用了一个循环来获取多个采样值,并且在每次采样之间进行了延迟。这是为了防止采样值在短时间内发生剧烈变化,导致过滤器无法正确地过滤数据。因此,延迟时间的长度应根据实际采样系统的特定情况进行调整。
4、限幅平均滤波
#include "stm32f4xx_hal.h"
#define SAMPLES 10 // 采样数据数量
#define MAX_VALUE 3000 // 最大值
#define MIN_VALUE 1000 // 最小值
ADC_HandleTypeDef hadc;
uint16_t samples[SAMPLES]; // 保存采样数据的数组
uint16_t filteredValue; // 过滤后的数据
uint16_t Sum=0 //累计平均值
// 限幅滤波器函数
uint16_t clampFilter(uint16_t value, uint16_t minVal, uint16_t maxVal)
{
if (value > maxVal) {
return maxVal;
}
else if (value < minVal) {
return minVal;
}
else {
return value;
}
}
int main(void)
{
HAL_Init();
// 初始化ADC
__HAL_RCC_ADC1_CLK_ENABLE();
hadc.Instance = ADC1;
hadc.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV2;
hadc.Init.Resolution = ADC_RESOLUTION_12B;
HAL_ADC_Init(&hadc);
// 配置ADC通道
ADC_ChannelConfTypeDef sConfig = {0};
sConfig.Channel = ADC_CHANNEL_0;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
HAL_ADC_ConfigChannel(&hadc, &sConfig);
// 连续采样
HAL_ADC_Start(&hadc);
// 采集多个样本
for (uint8_t i = 0; i < SAMPLES; i++)
{
HAL_ADC_PollForConversion(&hadc, 1000); // 等待采样完成
// 进行限幅滤波
filteredValue = clampFilter(HAL_ADC_GetValue(&hadc), MIN_VALUE, MAX_VALUE);
samples[i] = filteredValue; // 保存采样值
Sum+=(samples[i]/SAMPLES);
HAL_Delay(10); // 延迟10毫秒
}
printf("adc avg is:%.2f",Sum);
while (1)
{
// 循环执行其他代码
}
}
此示例从ADC采样器中获取多个采样值,保存在数组中。然后,通过使用限幅滤波器函数,将采样值限制在最大值和最小值区间内,得到过滤后的数据。注意,在示例中,使用了一个循环来获取多个采样值,并且在每次采样之间进行了延迟。这是为了防止采样值在短时间内发生剧烈变化,导致过滤器无法正确地过滤数据。因此,延迟时间的长度应根据实际采样系统的特定情况进行调整。