电量计新国标安全时间与保护记录系统架构
1. 概述
本文档系统说明基于 BST85226 电量计芯片的新国标安全时间和保护记录功能的系统架构设计。该系统实现了电池安全事件的实时检测、状态记录、Flash 持久化存储以及主机端数据获取的完整闭环。
1.1 系统架构总览
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| ┌─────────────────────────────────────────────────────────────────────────┐ │ 系统架构图 │ ├─────────────────────────────────────────────────────────────────────────┤ │ │ │ ┌──────────────────┐ I2C/SMBus ┌──────────────────────────┐ │ │ │ 主机 MCU │◄──────────────────►│ BST85226 电量计 │ │ │ │ (dev_bst7001.c) │ + PEC校验 │ (Firmware) │ │ │ │ │ │ │ │ │ │ ┌────────────┐ │ GPIO INT │ ┌────────────────────┐ │ │ │ │ │ 故障查询 │ │◄────────────────────│ │ 保护检测模块 │ │ │ │ │ │ SBS 0x8D │ │ 下降沿触发 │ │ (protect_handle) │ │ │ │ │ │ SBS 0x90 │ │ │ └────────────────────┘ │ │ │ │ └────────────┘ │ │ │ │ │ │ └──────────────────┘ │ ▼ │ │ │ │ ┌────────────────────┐ │ │ │ │ │ LOG 记录模块 │ │ │ │ │ │ (update_log_data) │ │ │ │ │ └────────────────────┘ │ │ │ │ │ │ │ │ │ ▼ │ │ │ │ ┌────────────────────┐ │ │ │ │ │ Flash 存储 │ │ │ │ │ │ (21KB 循环存储) │ │ │ │ │ └────────────────────┘ │ │ │ └──────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────┘
|
1.2 核心需求
根据新国标要求,电量计需要:
- 存储 4800 条异常记录(电芯最大 1200 循环 × 4 条异常记录)
- 记录异常类型、触发时间、异常数据、充放电状态、触发电芯 ID
- 支持主机按索引查询历史记录
2. LOG 数据结构设计
2.1 LOG_T 结构体定义
1 2 3 4 5 6 7 8 9
| typedef struct { uint16_t logindex:14; uint16_t charge_status:2; uint16_t raw:16; uint32_t timefw:24; uint32_t safety_status:4; uint32_t cell_id:4; } LOG_T;
|
结构体大小:8 字节(紧凑设计,满足存储效率要求)
2.2 字段说明
| 字段 |
位宽 |
说明 |
logindex |
14 bits |
日志索引,从0开始递增,支持循环覆盖 |
charge_status |
2 bits |
故障发生时的充放电状态 |
raw |
16 bits |
故障原始数据(电压mV/温度℃/电流mA) |
timefw |
24 bits |
相对时间戳(分钟),最大约31.92年 |
safety_status |
4 bits |
安全事件类型编码 |
cell_id |
4 bits |
OV/UV时标识触发的电芯编号 |
2.3 Safety Status 编码表
| 编码 |
事件类型 |
raw 字段含义 |
cell_id 含义 |
| 0x01 |
OV (过压) |
触发电芯电压 (mV) |
1=Cell1, 2=Cell2 |
| 0x02 |
UV (欠压) |
触发电芯电压 (mV) |
1=Cell1, 2=Cell2 |
| 0x03 |
OT (过温) |
温度 (℃) |
0 (不适用) |
| 0x04 |
UT (欠温) |
温度 (℃) |
0 (不适用) |
| 0x05 |
OCC/OCD (过流) |
电流 (mA) |
0 (不适用) |
| 0x06 |
SC (短路) |
电流 (mA) |
0 (不适用) |
| 0x07 |
ISC (内短路) |
漏电流 (mA) |
0 (不适用) |
3. 安全事件检测机制
3.1 事件触发流程图
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| ┌─────────────────────────────────────────────────────────────────────────┐ │ Safety Event 触发与记录流程 │ ├─────────────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────┐ │ │ │ 硬件检测 │ OV/UV/OT/UT (DFE 寄存器) │ │ └──────┬──────┘ │ │ │ │ │ ▼ │ │ ┌─────────────┐ ┌─────────────┐ │ │ │ protect_ │────►│ fault_imode │ 保存当前充放电状态 │ │ │ handle() │ │ = stm_check │ (在MOS关闭前捕获) │ │ └──────┬──────┘ │ _imode() │ │ │ │ └─────────────┘ │ │ ▼ │ │ ┌─────────────┐ │ │ │ update_log │ 设置标志位 │ │ │ = 1 │ │ │ └──────┬──────┘ │ │ │ │ │ ▼ │ │ ┌─────────────┐ │ │ │ 关闭 MOS │ Set_TXGPIO(0) 保护电路 │ │ └──────┬──────┘ │ │ │ │ │ ▼ (下一轮 mainloop) │ │ ┌─────────────┐ │ │ │ update_log_ │ 检测 safe_event,记录到 LOG_T │ │ │ data(1) │ 使用之前保存的 fault_imode │ │ └──────┬──────┘ │ │ │ │ │ ▼ │ │ ┌─────────────┐ │ │ │ write_log_ │ 写入 Flash (循环存储) │ │ │ into_flash()│ │ │ └──────┬──────┘ │ │ │ │ │ ▼ │ │ ┌─────────────┐ │ │ │ INT GPIO │ 下降沿通知主机有新故障 │ │ │ 触发 │ │ │ └─────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────┘
|
3.2 各类事件检测方式
3.2.1 硬件检测事件 (OV/UV/OT/UT)
这些事件由 DFE (Digital Front End) 硬件自动检测:
1 2 3 4 5 6 7 8 9 10
| if (hw_prot_new & GSTATUS_PROT_HWOVCELL1) { hw_prot_status.cell1_ov_status = 1; need_close_mos = 1; } if (hw_prot_new & GSTATUS_PROT_HWOVCELL2) { hw_prot_status.cell2_ov_status = 1; need_close_mos = 1; }
|
特点:
- 硬件实时检测,响应速度快
- 通过 DFE STATUS 寄存器读取状态
- 触发后立即关闭 MOS 保护电路
3.2.2 软件检测事件 (OCC/OCD)
过流保护由软件算法判断:
1 2 3 4 5 6 7
| if (occ_prot.status == 1 && occ_status_prev == 0) { need_close_mos = 1; } if (ocd_prot.status == 1 && ocd_status_prev == 0) { need_close_mos = 1; }
|
特点:
- 软件定时采样电流并判断
- 支持可配置的阈值和延时
- 状态变化触发保护
3.2.3 短路复位事件 (SC)
SC 是硬件级保护,触发后系统复位:
1 2 3 4 5 6 7 8 9
| log_init();
uint16_t rst_status = Chip_BLK_Get_Buffer(CH_BLANK2); if (rst_status & 0x08) { fault_imode = STM_IMODE_INDSG; update_log = 1; }
|
特点:
- 硬件保护触发系统复位
- 复位后从寄存器恢复 SC 标志
- 无法获取实际发生时的 charge_status,推断为放电状态
3.2.4 内短路检测事件 (ISC)
内短路通过 ISRW1 算法检测:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| #if __ISRW1_ENABLE__ isrw1_integration_update(); { static uint8_t isc_alert_prev = 0; uint32_t isc_result = isrw1_integration_get_alert_result(); uint8_t isc_alert_now = isc_result & 0xFF; if (isc_alert_now > 0 && isc_alert_prev == 0) { fault_imode = stm_check_imode(); update_log = 1; } isc_alert_prev = isc_alert_now; } #endif
|
特点:
- 基于电压-库仑法检测内部漏电流
- 分级告警 (mild/moderate/severe)
- 周期性检测,检测到新告警时记录
3.3 充放电状态捕获机制
为保证记录的是故障发生时的充放电状态,而非写入 LOG 时的状态:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| STM_IMODE_T fault_imode = STM_IMODE_IDLE;
if (need_close_mos) { fault_imode = stm_check_imode(); update_log = 1; Set_TXGPIO(0); }
|
4. Flash 存储设计
4.1 存储区域规划
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| ┌─────────────────────────────────────────────────────────────────┐ │ Flash 存储布局 (64KB) │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ Page 0-32: Bootloader & Firmware Code │ │ ────────────────────────────────────────────────────────────── │ │ Page 33: 持久化数据区 (1KB) │ │ - 激活信息 (activate_time, lifetime_en) │ │ - 校准数据 (cell offset, current slope) │ │ - 累计放电容量 (agemah) │ │ ────────────────────────────────────────────────────────────── │ │ Page 34-52: LOG1 区域 (19KB) │ │ - 19 × 1024 / 8 = 2432 条记录 │ │ ────────────────────────────────────────────────────────────── │ │ Page 53-60: Reserved │ │ ────────────────────────────────────────────────────────────── │ │ Page 61-62: LOG2 区域 (2KB) │ │ - 2 × 1024 / 8 = 256 条记录 │ │ ────────────────────────────────────────────────────────────── │ │ Page 63: Reserved │ │ │ │ 总 LOG 容量: 21KB / 8B = 2688 条 (满足 4800 条需求需扩展) │ └─────────────────────────────────────────────────────────────────┘
|
4.2 循环写入策略
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| void write_log_into_flash(void) { uint16_t total_log_capacity = FLASH_LOG_DATA_SIZE / sizeof(LOG_T); uint16_t circular_index = log_p->logindex % total_log_capacity; uint16_t log1_capacity = FLASH_LOG1_DATA_SIZE / sizeof(LOG_T); uint16_t area_offset;
if (circular_index < log1_capacity) { page_log_now = PAGE_LOG1_START; area_offset = circular_index * sizeof(LOG_T); } else { page_log_now = PAGE_LOG2_START; area_offset = (circular_index - log1_capacity) * sizeof(LOG_T); }
uint16_t page_offset = area_offset / FLASH_PAGE_SIZE; uint16_t offset_in_page = area_offset % FLASH_PAGE_SIZE; page_log_now += page_offset;
if (0 == offset_in_page) { eflash_erase_page(page_log_now); } eflash_mem_to_flash(page_log_now, offset_in_page, sizeof(LOG_T), (uint8_t *)log_mem); log_p->logindex++; }
|
设计要点:
logindex 从 0 开始,持续递增
- 使用取模运算实现循环覆盖
- 页边界自动擦除,无需预读保护
- 支持跨区域连续存储
4.3 LOG 读取接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| int16_t read_log_by_index(uint16_t logindex, LOG_T *log_data) { if (log_p->logindex > total_log_capacity) { uint16_t oldest_valid = log_p->logindex - total_log_capacity; if (logindex < oldest_valid) { return -1; } } uint16_t circular_index = logindex % total_log_capacity; if (log_data->logindex != logindex) { return -1; } return 0; }
|
5. 主机驱动接口
5.1 SBS 寄存器定义
| 寄存器地址 |
名称 |
读写 |
长度 |
说明 |
| 0x8D |
LATESTLOG |
R |
8B |
读取最新一条 LOG 记录 |
| 0x90 |
LOGBYINDEX |
R/W |
8B |
按索引查询 LOG (写2B索引,读8B数据) |
| 0x1F |
SAFETYSTATUS |
R |
2B |
当前安全状态标志 |
5.2 驱动代码示例
5.2.1 获取最新故障记录
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
|
static bool batt_get_latest_fault(dev_id_t id, fault_record_t* fault_record) { int32_t ret = 0; uint8_t tempBuff[8] = {0};
ret = BST7001_read_bytes(id, SBS8D_LATESTLOG, tempBuff, 8); if (ret < 0) return false;
uint16_t word0 = (uint16_t)tempBuff[0] | ((uint16_t)tempBuff[1] << 8); fault_record->logindex = word0 & 0x3FFF; fault_record->charge_status = (word0 >> 14) & 0x03; fault_record->rawData = (int16_t)((uint16_t)tempBuff[2] | ((uint16_t)tempBuff[3] << 8)); fault_record->timefw = (uint32_t)tempBuff[4] | ((uint32_t)tempBuff[5] << 8) | ((uint32_t)tempBuff[6] << 16); fault_record->safetystatus = tempBuff[7] & 0x0F; fault_record->cellID = (tempBuff[7] >> 4) & 0x0F; return true; }
|
5.2.2 按索引查询历史记录
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
|
static bool batt_get_fault_by_index(dev_id_t id, uint16_t logindex, fault_record_t* fault_record) { int32_t ret = 0; uint8_t writeBuff[2] = {0}; uint8_t readBuff[8] = {0};
writeBuff[0] = (uint8_t)(logindex & 0xFF); writeBuff[1] = (uint8_t)((logindex >> 8) & 0xFF); ret = BST7001_write_bytes(id, SBS90_LOGBYINDEX, writeBuff, 2); if (ret < 0) return false;
ret = BST7001_read_bytes(id, SBS90_LOGBYINDEX, readBuff, 8); if (ret < 0) return false;
if (readBuff[0] == 0xFF && readBuff[1] == 0xFF && ) { return false; }
return true; }
|
5.2.3 GPIO 中断处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| hsd->pin_int.id = IO_BST7001_INT; hsd->pin_int.active_val = BST7001_GPIO_ACTIVE_LOW; drv_io_port.quick_init(hsd->pin_int.id, GPIO_MODE_INPUT, GPIO_PULL_UP); drv_io_port.quick_interrupt_config(hsd->pin_int.id, EXTI_TRIGGER_FALLING, 0x02);
void BST7001_INT_IRQHandler(void) { fault_record_t fault; if (batt_get_latest_fault(dev_id, &fault)) { process_battery_fault(&fault); } }
|
5.3 主机端使用示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| fault_record_t latest_fault; if (batt_get_latest_fault(id, &latest_fault)) { printf("Latest fault: idx=%d, type=%d, time=%d min\n", latest_fault.logindex, latest_fault.safetystatus, latest_fault.timefw); }
for (uint16_t i = 0; i <= latest_fault.logindex; i++) { fault_record_t record; if (batt_get_fault_by_index(id, i, &record)) { printf("Log[%d]: safety=0x%X, raw=%d, charge=%d\n", record.logindex, record.safetystatus, record.rawData, record.charge_status); } }
|
6. 时间戳系统
6.1 时间基准
- 基准时间: 2025-01-01 00:00:00
- 单位: 分钟
- 位宽: 24 bits
- 最大范围: 16,777,215 分钟 ≈ 31.92 年
6.2 时间同步机制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
void log_init_activate_time(uint32_t activate_time) { time_activate = activate_time; }
void log_update_time(uint32_t calitime) { if (calitime) { timefw_now = calitime & 0xFFFFFF; } }
void log_sync_time(uint32_t minutes) { timefw_now += minutes; timefw_now &= 0xFFFFFF; }
|
6.3 时间用途
- 故障定位: 记录故障发生的精确时间点
- 寿命管理: 计算电池使用时长
- CV 调整: 超过一定年限后自动降低充电截止电压
1 2 3 4 5 6 7 8 9
|
if (timefw_now - time_activate > 525960) { fg_chg_update(log_cfg, design_cv - 100, ...); } else if (timefw_now - time_activate > 1051920) { fg_chg_update(log_cfg, design_cv - 150, ...); }
|
7. 系统可靠性设计
7.1 通信校验
主机驱动采用 SMBus PEC (Packet Error Checking) 校验:
1 2 3 4 5 6 7 8 9 10 11
| static uint8_t calculate_pec_byte(uint8_t data, uint8_t crcin) { uint8_t crc8 = data ^ crcin; for (uint8_t i = 0; i < 8; i++) { if (crc8 & 0x80) crc8 = (crc8 << 1) ^ I2C_PEC_POLY; else crc8 = (crc8 << 1); } return crc8; }
|
7.2 重试机制
1 2 3 4 5 6 7
| for (i = 0; i < 4; i++) { ret = dev_recv(hsd->fd_dev_ops, rxbuf, read_len + 1); if (ret && rxbuf[read_len] == calculated_pec) { break; } }
|
7.3 数据完整性
- LOG 记录包含
logindex 用于验证数据有效性
- 读取时比对
logindex 是否匹配请求的索引
- 无效数据返回全 0xFF
8. 扩展与优化建议
8.1 存储容量扩展
当前设计支持约 2688 条记录,如需满足 4800 条需求:
- 方案1: 扩展 LOG2 区域或增加 LOG3 区域
- 方案2: 压缩 LOG_T 结构(如时间精度降低)
- 方案3: 使用外部 Flash 存储
8.2 事件优先级
建议实现事件优先级机制:
- Critical: SC, ISC (立即记录)
- High: OV, UV, OCC, OCD (立即记录)
- Medium: OT, UT (可合并相近时间的事件)
- Low: 周期性状态记录
8.3 主机端缓存
建议主机端实现本地缓存机制:
- 定期同步新增记录
- 断电恢复后只需同步增量
- 减少 I2C 通信开销
9. 总结
本系统实现了完整的电池安全事件检测、记录和查询功能:
| 功能模块 |
实现方式 |
关键技术 |
| 事件检测 |
硬件+软件混合 |
DFE 寄存器、软件算法 |
| 状态捕获 |
实时保存 |
fault_imode 机制 |
| 数据存储 |
Flash 循环 |
页边界擦除、跨区域存储 |
| 主机查询 |
SBS 接口 |
PEC 校验、中断通知 |
| 时间管理 |
24-bit 时间戳 |
主机校准、分钟精度 |
该设计满足新国标对电池安全记录的要求,具备良好的可靠性和扩展性。
附录 A: 相关文件清单
| 文件 |
说明 |
flash/log.c |
LOG 模块核心实现 |
flash/log.h |
LOG 数据结构定义 |
user/main.c |
主循环、保护检测 |
flash/stm.c |
状态机、充放电状态判断 |
dev_bst7001.c |
主机 MCU 驱动 |
dev_bst7001.h |
驱动接口定义 |
附录 B: 修订历史
| 版本 |
日期 |
修改内容 |
| V1.0 |
2026-01-30 |
初始版本 |