2. 电压采集模块实现

任务说明

本案例实现BMS中电芯单体电压采集模块,覆盖三个核心环节:采样(通过ADC读取原始值)、滤波(滑动平均去除噪声)、故障诊断(判断电压是否越限或传感器断线)。输入为模拟的ADC原始读数序列,输出为滤波后的电压值及诊断状态。

模块设计

电压采集模块按以下数据流工作:ADC原始值 → 标定换算为电压 → 滑动平均滤波 → 阈值诊断 → 输出结果。故障诊断覆盖三种情况:电压过高、电压过低、传感器断线(ADC值为0或满量程时判定为异常)。

电压采集模块数据流 ADC采样 原始值 0~4095 标定换算 V = raw × K 滑动平均滤波 窗口 N=5 故障诊断 过压/欠压/断线 输出 电压+状态 诊断阈值:过压 > 4.2V | 欠压 < 2.8V | 断线:ADC=0 或 ADC=4095 滤波窗口:5点滑动平均

代码实现


#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>

#define ADC_MAX         4095
#define ADC_MIN         0
#define VOLT_SCALE      0.001f      /* 每LSB对应电压,单位V */
#define FILTER_WINDOW   5
#define OVP_THRESHOLD   4.2f        /* 过压阈值 */
#define UVP_THRESHOLD   2.8f        /* 欠压阈值 */

typedef enum {
    DIAG_NORMAL = 0,
    DIAG_OVP,
    DIAG_UVP,
    DIAG_OPEN_WIRE
} diag_status_t;

typedef struct {
    float voltage;          /* 滤波后电压值 */
    diag_status_t status;   /* 诊断状态 */
} cell_volt_result_t;

/* 滑动平均滤波器 */
typedef struct {
    float buffer[FILTER_WINDOW];
    uint8_t index;
    uint8_t count;
} moving_avg_t;

static void filter_init(moving_avg_t *f) {
    f->index = 0;
    f->count = 0;
    for (int i = 0; i < FILTER_WINDOW; i++) {
        f->buffer[i] = 0.0f;
    }
}

static float filter_add(moving_avg_t *f, float value) {
    f->buffer[f->index] = value;
    f->index = (f->index + 1) % FILTER_WINDOW;
    if (f->count < FILTER_WINDOW) {
        f->count++;
    }
    float sum = 0.0f;
    for (int i = 0; i < f->count; i++) {
        sum += f->buffer[i];
    }
    return sum / (float)f->count;
}

/* 故障诊断 */
static diag_status_t diagnose(float voltage, uint16_t raw_adc) {
    if (raw_adc == ADC_MAX || raw_adc == ADC_MIN) {
        return DIAG_OPEN_WIRE;   /* 断线或短路 */
    }
    if (voltage > OVP_THRESHOLD) {
        return DIAG_OVP;
    }
    if (voltage < UVP_THRESHOLD) {
        return DIAG_UVP;
    }
    return DIAG_NORMAL;
}

/* 电压采集主函数 */
cell_volt_result_t cell_volt_sample(uint16_t adc_raw, moving_avg_t *filter) {
    cell_volt_result_t result;
    float volt_raw = (float)adc_raw * VOLT_SCALE;
    result.voltage = filter_add(filter, volt_raw);
    result.status = diagnose(result.voltage, adc_raw);
    return result;
}

/* 状态文本映射 */
const char* status_to_str(diag_status_t s) {
    switch (s) {
        case DIAG_NORMAL:    return "NORMAL";
        case DIAG_OVP:       return "OVP";
        case DIAG_UVP:       return "UVP";
        case DIAG_OPEN_WIRE: return "OPEN_WIRE";
        default:             return "UNKNOWN";
    }
}

int main(void) {
    /* 模拟ADC采样序列:包含正常、过压、欠压、断线场景 */
    uint16_t adc_samples[] = {
        3500, 3510, 3495, 3505, 3500,   /* 正常 ~3.5V */
        4300, 4310, 4295, 4305, 4300,   /* 过压 ~4.3V */
        2500, 2510, 2495, 2505, 2500,   /* 欠压 ~2.5V */
        0,    0,    0,    0,    0,      /* 断线 */
        4095, 4095, 4095, 4095, 4095    /* 断线 */
    };
    int num_samples = sizeof(adc_samples) / sizeof(adc_samples[0]);
    
    moving_avg_t filter;
    filter_init(&filter);
    
    printf("=== 电压采集模块测试 ===\n");
    for (int i = 0; i < num_samples; i++) {
        cell_volt_result_t res = cell_volt_sample(adc_samples[i], &filter);
        printf("样本[%2d] ADC=%4d → 电压=%.3fV 状态=%s\n",
               i, adc_samples[i], res.voltage, status_to_str(res.status));
    }
    return 0;
}

编译与运行


gcc -o voltage_module voltage_module.c -std=c99
./voltage_module

验证方法

程序使用预定义的ADC采样序列模拟五种工况。判定标准如下:

  • 前5个样本(ADC≈3500)滤波后电压约3.5V,状态为NORMAL;
  • 第6~10个样本(ADC≈4300)电压约4.3V,状态为OVP;
  • 第11~15个样本(ADC≈2500)电压约2.5V,状态为UVP;
  • 第16~20个样本(ADC=0)及第21~25个样本(ADC=4095)状态为OPEN_WIRE;
  • 滤波窗口生效:每个场景的第1个样本输出为当前值(窗口未满),第2个样本起为2点平均,第5个样本起为完整5点平均。

运行程序后,观察输出中每个样本的电压值与状态是否符合上述预期。若所有场景的状态判定与电压范围正确,且滤波后电压随窗口填充逐渐平滑,则验证通过。