2. 电压采集模块实现
任务说明
本案例实现BMS中电芯单体电压采集模块,覆盖三个核心环节:采样(通过ADC读取原始值)、滤波(滑动平均去除噪声)、故障诊断(判断电压是否越限或传感器断线)。输入为模拟的ADC原始读数序列,输出为滤波后的电压值及诊断状态。
模块设计
电压采集模块按以下数据流工作:ADC原始值 → 标定换算为电压 → 滑动平均滤波 → 阈值诊断 → 输出结果。故障诊断覆盖三种情况:电压过高、电压过低、传感器断线(ADC值为0或满量程时判定为异常)。
代码实现
#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点平均。
运行程序后,观察输出中每个样本的电压值与状态是否符合上述预期。若所有场景的状态判定与电压范围正确,且滤波后电压随窗口填充逐渐平滑,则验证通过。