3. 电流采集模块实现

电流采集模块实现 学习卡片

任务说明

本案例实现BMS中的电流采集模块,完成双向电流检测(充放电方向识别)与过流保护功能。输入为模拟电流传感器信号(通过ADC采样值模拟),输出为带符号的电流值(正为放电、负为充电)以及过流保护触发信号。模块采用周期性采样与软件滤波,当电流值超过阈值时置位过流标志并记录故障。

模块设计

电流采集模块包含三个核心部分:ADC采样与标定(将原始ADC值转换为实际电流)、双向电流判定(根据符号判断充放电状态)、过流保护逻辑(比较值与阈值)。模块对外提供三个接口函数:初始化、周期采集处理、获取状态。采样使用中值滤波消除单次毛刺,标定采用线性映射(含零点偏移与比例系数)。

ADC采样 原始值(0~4095) 中值滤波 窗口=5 标定转换 电流 = k*raw + b 过流判断 |I| > I_threshold 状态输出 电流值+方向+故障 来自电流传感器 → 上层BMS逻辑 关键参数 k: 0.125 A/LSB b: -250.0 A I_th: 150.0 A Vref: 3.3V ADC: 12-bit 采样周期: 10ms

代码实现


// current_sensor.h
#ifndef CURRENT_SENSOR_H
#define CURRENT_SENSOR_H

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

#define ADC_MAX_VALUE     4095.0f
#define ADC_VREF_VOLT     3.3f
#define SENSOR_SCALE_K    0.125f    // A/LSB(含传感器灵敏度与放大倍数)
#define SENSOR_OFFSET_B   -250.0f   // 零点偏移(A)
#define OVERCURRENT_TH_A  150.0f    // 过流阈值(A)
#define FILTER_WINDOW     5         // 中值滤波窗口

typedef enum {
    CURRENT_DIR_CHARGING = 0,   // 负电流,充电
    CURRENT_DIR_DISCHARGING = 1 // 正电流,放电
} current_dir_t;

typedef struct {
    float current_a;            // 实际电流(A),正=放电,负=充电
    current_dir_t direction;    // 电流方向
    bool overcurrent_flag;      // 过流标志
    float raw_adc_voltage;      // ADC电压(V)
} current_sensor_data_t;

// 初始化传感器模块
void current_sensor_init(void);

// 周期调用:传入ADC原始值,更新采集结果
void current_sensor_process(uint16_t adc_raw, current_sensor_data_t *out);

#endif

// current_sensor.c
#include "current_sensor.h"
#include <stdlib.h>

static uint16_t filter_buffer[FILTER_WINDOW];
static uint8_t buffer_index = 0;
static uint8_t buffer_count = 0;

// 中值滤波比较函数
static int cmp_uint16(const void *a, const void *b) {
    return (*(uint16_t*)a - *(uint16_t*)b);
}

// 获取滤波窗口内的中值
static uint16_t median_filter(uint16_t new_sample) {
    uint16_t temp[FILTER_WINDOW];
    filter_buffer[buffer_index] = new_sample;
    buffer_index = (buffer_index + 1) % FILTER_WINDOW;
    if (buffer_count < FILTER_WINDOW) buffer_count++;

    // 复制有效数据到临时数组
    uint8_t valid_count = (buffer_count < FILTER_WINDOW) ? buffer_count : FILTER_WINDOW;
    uint8_t start = (buffer_index + FILTER_WINDOW - valid_count) % FILTER_WINDOW;
    for (uint8_t i = 0; i < valid_count; i++) {
        temp[i] = filter_buffer[(start + i) % FILTER_WINDOW];
    }
    qsort(temp, valid_count, sizeof(uint16_t), cmp_uint16);
    return temp[valid_count / 2];
}

void current_sensor_init(void) {
    buffer_index = 0;
    buffer_count = 0;
    for (uint8_t i = 0; i < FILTER_WINDOW; i++) {
        filter_buffer[i] = 0;
    }
}

void current_sensor_process(uint16_t adc_raw, current_sensor_data_t *out) {
    if (out == NULL) return;

    // 1. 中值滤波
    uint16_t filtered = median_filter(adc_raw);

    // 2. 转换为电压
    float voltage = (float)filtered / ADC_MAX_VALUE * ADC_VREF_VOLT;

    // 3. 标定:ADC原始值直接映射到电流(含零点与比例)
    //    实际项目中电压→电流的线性关系已折算进SENSOR_SCALE_K与SENSOR_OFFSET_B
    float current = (float)filtered * SENSOR_SCALE_K + SENSOR_OFFSET_B;

    // 4. 方向判定
    current_dir_t dir = (current >= 0) ? CURRENT_DIR_DISCHARGING : CURRENT_DIR_CHARGING;

    // 5. 过流判断
    bool overcurrent = (fabsf(current) > OVERCURRENT_TH_A) ? true : false;

    // 6. 输出
    out->current_a = current;
    out->direction = dir;
    out->overcurrent_flag = overcurrent;
    out->raw_adc_voltage = voltage;
}

// main.c 示例调用
#include <stdio.h>
#include "current_sensor.h"

int main(void) {
    current_sensor_data_t sensor_data;
    current_sensor_init();

    // 模拟一组ADC采样值(12位)
    // 2000 → 0A附近;3200 → 150A(放电);800 → -150A(充电)
    uint16_t test_samples[] = {2000, 2005, 1998, 2002, 2001,   // 约0A
                               3200, 3205, 3198, 3202, 3201,   // 约150A放电
                               800,  805,  798,  802,  801};   // 约-150A充电

    printf("=== 电流采集模块测试 ===\r\n");
    for (int i = 0; i < 15; i++) {
        current_sensor_process(test_samples[i], &sensor_data);
        if (i % 5 == 4) {  // 每组最后一个采样后打印
            const char* dir_str = (sensor_data.direction == CURRENT_DIR_DISCHARGING) ? "放电" : "充电";
            printf("组%d: 电流=%.1fA [%s] 过流=%s\r\n",
                   i/5 + 1,
                   sensor_data.current_a,
                   dir_str,
                   sensor_data.overcurrent_flag ? "是" : "否");
        }
    }
    return 0;
}

编译与运行


gcc -o current_sensor_test main.c current_sensor.c -lm
./current_sensor_test

验证方法

编译并运行上述程序,观察三组测试输出。第一组ADC值在2000附近,预期电流接近0A,方向为放电(因计算值略大于0),过流标志为否;第二组ADC值在3200附近,预期电流约150A,方向为放电,过流标志为是(因150.0A恰好等于阈值,使用>判断故触发);第三组ADC值在800附近,预期电流约-150A,方向为充电,过流标志为是。若输出与上述逻辑一致,且滤波后数值稳定(每组内波动小于1A),则判定模块功能正常。

判定标准:① 方向判定正确区分正负电流;② 过流标志在|I|>150A时置位;③ 中值滤波有效抑制单次异常采样(可手动将某组中一个值改为4095验证输出不受影响)。