基于STM32HAL库的ADC采样滤波程序

本文介绍了单片机中常用的三种ADC采样滤波算法:平均值滤波、简单滑动平均滤波、中值滤波以及限幅平均滤波,展示了它们的原理和在STM32F4xx平台上的实现方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在单片机中,常用的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采样器中获取多个采样值,保存在数组中。然后,通过使用限幅滤波器函数,将采样值限制在最大值和最小值区间内,得到过滤后的数据。注意,在示例中,使用了一个循环来获取多个采样值,并且在每次采样之间进行了延迟。这是为了防止采样值在短时间内发生剧烈变化,导致过滤器无法正确地过滤数据。因此,延迟时间的长度应根据实际采样系统的特定情况进行调整。

### STM32 HAL ADC 采样中的滤波功能实现 在STM32微控制器中,通过HAL可以方便地配置和操作ADC模块。为了实现ADC采样滤波功能,通常可以通过硬件或软件的方式完成。 #### 硬件方式 某些型号的STM32芯片内置了可编程数字滤波器来处理ADC采样数据。例如,在一些高级系列(如STM32F7、STM32H7)中,ADC支持过采样模式(Oversampling Mode)。这种模式允许通过对多次转换的结果求平均值来降低噪声并提高分辨率[^2]。启用此功能的方法如下: 1. 配置ADC初始化结构体`ADC_HandleTypeDef`的相关参数。 2. 设置`OverSamplingMode`为`ENABLE`。 3. 定义具体的过采样比率(`RightBitShift`),这决定了最终结果的有效位数以及平均计算次数。 以下是基于HAL的一个简单代码示例展示如何开启ADC采样功能: ```c // 初始化ADC句柄 ADC_HandleTypeDef hadc; void MX_ADC_Init(void) { // 基本配置... // 启用过采样模式 hadc.Instance = ADC1; hadc.Init.Oversampling.Mode = ENABLE; // 开启过采样 hadc.Init.Oversampling.Ratio = ADC_OVERSAMPLING_RATIO_8; // 过采样比例设为8次 hadc.Init.Oversampling.RightBitShift = ADC_RIGHTBITSHIFT_NONE;// 不移除任何低位 if (HAL_ADC_Init(&hadc) != HAL_OK){ Error_Handler(); } } ``` 对于不支持内部过采样的设备,则需要依赖外部电路或者纯软件算法来进行信号平滑化处理。 #### 软件方法 当目标MCU缺乏上述提到的功能时,可以在应用层面上编写自己的过滤逻辑。常见的做法包括但不限于移动平均法(Moving Average Filter),指数加权移动平均(IIR Low Pass Filter)等技术手段之一种或多者组合运用以达到理想效果。 下面给出一段利用简单的IIR低通滤波器的例子: ```c #define FILTER_COEFFICIENT 0.9f // 滤波系数, 取决于具体需求调整大小 float filtered_value = 0.0f; // 存储上一时刻经过滤后的数值 uint16_t read_adc_and_filter(ADC_HandleTypeDef *adc_handle){ uint16_t raw_data; static float previous_output = 0.0f; /* 执行一次新的测量 */ HAL_ADC_Start(adc_handle); HAL_ADC_PollForConversion(adc_handle, HAL_MAX_DELAY); raw_data = HAL_ADC_GetValue(adc_handle); /* 应用IIR LPF公式更新当前输出 */ previous_output = ((FILTER_COEFFICIENT)*previous_output + (1-(FILTER_COEFFICIENT))*(raw_data)); return (uint16_t)(previous_output); } /* 使用该函数代替直接读取原始ADC值的地方 */ while(1){ uint16_t smooth_result = read_adc_and_filter(&hadc); ... } ``` 以上片段展示了怎样构建一个基本的一阶无限脉冲响应(Infinite Impulse Response,IIR)型低通滤波器用于连续监测环境变量变化情况下的平稳估计过程之中去减少随机干扰影响程度从而获得更加精确可靠的数据表示形式出来供后续分析决策所用[^3]. ### 注意事项 - 在实际项目开发过程中还需要考虑实时性能要求等因素的影响关系之间做出适当平衡优化措施才行得通顺合理有效果显著提升整体系统稳定性表现水平上去满足预期设计指标范围之内即可成功解决问题啦! 相关问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值