现场平衡仪的电池监测模块负责读取电量计、维护数据有效性,并通过串口向主机报告。它与充电器、保护电路共同工作;STM32 读到了 SOC,并不等于已经实现电芯过充、过放、过流或温度保护。
原记录描述了一个 STM32F103 智能电池接口方案,但没有附电量计型号、实际工程、原理图或验收数据。本文保留其接口和开发分工,将未经测量的指标标为目标,并补齐串口格式的歧义。下文“协议建议”是可复算的设计示例,接入旧设备前仍需核对实际固件,不能假定其已经采用这些规则。
1. 原项目配置与待确认项
跳转到“1. 原项目配置与待确认项”| 项目 | 原记录 | 设计时需要确认 |
|---|---|---|
| 控制器 | STM32F103C8T6,48引脚、72 MHz | C8 的 Flash/RAM、时钟、调试与所需外设资源 |
| 电池 | 18650、2S2P、5000 mAh、标称7.2 V | 电芯化学体系、串并联配置、保护板、电量计型号及额定条件 |
| 充电器 | 8.4 V / 2 A | 与具体2串电池的充电策略、温度范围和电流能力匹配;“低干扰”需要测量 |
| 电量链路 | I²C1,100 kHz | 实际是普通I²C还是SMBus,命令集、电平、超时、PEC和地址约定 |
| 主机链路 | USART1,115200、8N1,经CH340接USB | TX/RX交叉、电平、共地或隔离方式;USB在桥接器侧 |
| 调度目标 | 500 ms读取、1000 ms上报 | 总线超时不阻塞上报,过期数据不冒充新数据 |
默认未重映射时,I²C1 使用 PB6/SCL、PB7/SDA;USART1 使用 PA9/TX、PA10/RX。引脚复用和电气范围应按 STM32F103x8/xB 数据手册配置。电池包标称7.2 V不能直接接到 MCU 的通信引脚。
2S2P 表示两组串联,每组两节并联。若每节额定2500 mAh、3.6 V,理想组合得到5000 mAh、7.2 V,标称能量约36 Wh。实际可用能量还受截止电压、温度、老化、负载和转换损耗影响,不能直接拿容量标称值保证续航。假定可用能量恰为36 Wh、供电效率85%、负载恒定6 W,估算续航为5.1小时;这是算术示例。
原文的 SOC“±1%”、CPU“<5%”、模块电流“<50 mA”均缺少测试条件,保留为待验证目标。电量计读数的分辨率、重复性与绝对精度也不是同一个概念。TI 电量计概览区分了通信接口和带/不带集成保护的器件,选型必须落实到料号。
2. 电池侧协议不能靠“I²C”三个字推断
跳转到“2. 电池侧协议不能靠“I²C”三个字推断”首先获得电池或电量计手册,确认地址、寄存器宽度、读序列、字节顺序、符号、量纲、状态位及唤醒条件。I²C与SMBus使用相关的双线机制,但电气、时序、超时和命令语义存在区别。上拉电阻应由电压、总线电容、上升时间和灌电流确定,原记录的4.7 kΩ只能作为候选值。NXP I²C 规范
只有设备声明兼容 SBS 时,才可使用以下 Smart Battery Data 1.1命令作为核对起点:
| 命令 | 代码 | 解释 |
|---|---|---|
| BatteryMode | 0x03 | 包含容量表示模式 |
| Temperature | 0x08 | 0.1 K,换算℃为 raw / 10 - 273.15 |
| Voltage | 0x09 | mV |
| Current | 0x0A | 有符号mA,正充电、负放电 |
| RelativeStateOfCharge | 0x0D | 百分数 |
| RemainingCapacity | 0x0F | 依模式为mAh或10 mWh,不能固定当mAh |
| BatteryStatus | 0x16 | SBS状态字,不是本页自定义串口状态字节 |
SMBus Read Word 在线路上先传低字节;下文UART示例使用大端字段,两者应显式转换。PEC如被使用,应按SMBus规则计算,不能拿UART的加法校验代替。SMBus 3.3.1 规范
SOC读失败不能返回0来表示掉线,否则主机会把通信故障解释为电量耗尽。每次读取应得到值、错误状态和更新时间;超时、NACK、PEC错误、范围异常分别计数。对多个独立寄存器的读取还应记录读取时间范围,避免声称所有值在同一瞬间采样。
3. 软件分层与调度
跳转到“3. 软件分层与调度”flowchart LR Pack["电池包与电量计"] --> Bus["I2C 或 SMBus 驱动"] Bus --> Decode["型号适配与单位转换"] Decode --> Snapshot["数据快照、有效位、时间戳"] Snapshot --> App["调度、告警与查询"] App --> Frame["串口编码与发送队列"] Frame --> UART["USART 与 CH340"] UART --> Host["主机显示与日志"]原项目名为powershow,可保留以下职责划分。它是结构建议,本页没有附带可直接构建的完整工程:
powershow/ Core/Inc/app_config.h 配置、数据与有效性定义 Core/Inc/battery.h 电池接口契约 Core/Inc/usb_protocol.h 历史命名,实际为UART帧协议 Core/Src/battery.c 型号适配、读取和转换 Core/Src/usb_protocol.c 收帧、编码和发送队列 Core/Src/app_task.c 500/1000 ms等周期调度 Drivers/ HAL与CMSIS Doc/ 协议、接线、测试记录 tools/battery_monitor.py 计划中的上位机工具 CMakeLists.txt 需配合Arm工具链和链接脚本原接口Battery_Init、Battery_ReadAll、Battery_IsConnected可以保留;Battery_GetSOC应同时返回成功/失败,不能只用一个无符号数掩盖错误。USB_Protocol_Init、USB_SendBatteryData、USB_ProcessReceivedData、USB_AutoReport是旧命名,职责分别是初始化、编码排队、解析和周期上报;新工程可改成UART_前缀。
应用层维护不可变快照,主机发起查询时读取最近一次有效快照,不在串口中断里同步访问电池。DMA发送期间,发送缓冲必须保持有效且不得修改;忙时进入有上限的发送队列。接收中断只搬运/通知,解析放在主循环或任务中。
毫秒计数回卷可用无符号差值判断经过时间,但要保证调度器正常轮询、所比较间隔在设计范围内。周期更改应规定范围、何时生效、是否持久保存和应答格式,不能仅写“支持配置”便认为协议完整。
4. 原帧格式的歧义与一致化建议
跳转到“4. 原帧格式的歧义与一致化建议”保留原来的帧布局:
AA | CMD:1 | LEN:1 | PAYLOAD:LEN | CHECK:1下面为本页建议的校验约定:CHECK = -(CMD + LEN + sum(PAYLOAD)) mod 256,不包含帧头AA;接收时从CMD到CHECK所有字节之和模256应为0。因此无数据查询帧是:
AA 01 00 FF原文写的是AA 01 00 01,同时又给出“取反加1”的算法,二者不一致;也没有明确校验覆盖范围。若旧固件实际上使用普通累加或包含帧头,就必须按旧规则兼容或升级双方,不能仅改上位机。
| CMD | 原意与本页范围 |
|---|---|
0x01 | PC查询时LEN=0;设备电池应答LEN=9 |
0x02 | 设置上报周期;建议有效载荷为大端uint16毫秒,但有效范围、应答与持久化仍须协议双方确认 |
0x03 | 查询系统状态;原文未定义应答负载,本页不编造已有设备格式 |
0x81 | 自动上报电池数据,LEN=9 |
九字节电池数据明确按字段编码,不能直接发送C结构体内存:
| 偏移 | 字段 | 编码 |
|---|---|---|
| 0 | SOC | uint8,有效值0~100 |
| 1~2 | 电压 | 大端uint16,mV |
| 3~4 | 电流 | 大端16位二进制补码,mA,负值表示放电 |
| 5~6 | 剩余容量 | 大端uint16,mAh;源为能量模式时不能直接填入 |
| 7 | 温度 | 8位二进制补码,整数℃;舍入规则和溢出处理需约定 |
| 8 | 状态 | bit0充电、bit1放电、bit2满电、bit3低电量;高四位保留 |
主机应检查状态矛盾和数据范围。低电量20%是原设计告警阈值,可加恢复滞回;它不是保护电路的欠压切断条件。原九字节负载没有有效位和数据年龄,下一版应明确加入版本、序号、有效位及时间信息。旧版失败时至少停止发送伪造的“新数据”,主机根据超时标为过期。
例如SOC=75%、7200 mV、-500 mA、2500 mAh、25℃、放电状态,其自动上报完整帧为:
AA 81 09 4B 1C 20 FE 0C 09 C4 19 02 FD13字节在115200、8N1下需要约1.13 ms线速时间,占每秒一次上报带宽约0.113%。因此该吞吐量不要求DMA;使用DMA的意义在于减少阻塞、统一驱动或适应未来负载,而不是因为普通发送必然不够快。
5. 可运行的编码与解码示例
跳转到“5. 可运行的编码与解码示例”以下Python只演示上述建议协议,不访问真实串口,不实现未定义的系统状态应答。decode_battery_frame接收已经分离出的完整帧;实际串口读取还必须处理分片、粘包、超时和重新找帧头。
import struct
BATTERY = struct.Struct(">BHhHbB")
def encode_frame(command, payload=b""): if not 0 <= command <= 255 or len(payload) > 255: raise ValueError("command or length out of range") body = bytes((command, len(payload))) + bytes(payload) return b"\xAA" + body + bytes((-sum(body) & 0xFF,))
def decode_battery_frame(frame): if len(frame) != 13 or frame[0] != 0xAA: raise ValueError("invalid frame boundary") if frame[1] not in (0x01, 0x81) or frame[2] != BATTERY.size: raise ValueError("not a battery response") if sum(frame[1:]) & 0xFF: raise ValueError("checksum mismatch") soc, mv, ma, mah, temp, status = BATTERY.unpack(frame[3:-1]) if soc > 100 or status & 0xF0 or status & 3 == 3: raise ValueError("invalid SOC or status bits") return dict(soc=soc, voltage_mv=mv, current_ma=ma, capacity_mah=mah, temperature_c=temp, status=status)
if __name__ == "__main__": query = encode_frame(0x01) report = encode_frame(0x81, BATTERY.pack(75, 7200, -500, 2500, 25, 2)) assert query.hex(" ") == "aa 01 00 ff" print(report.hex(" ")) print(decode_battery_frame(report))加法校验能发现部分错误,却不能可靠发现字节调换或相互抵消的多字节变化。需要更强检错能力时,可以另定义带版本的CRC协议,并准备黄金帧验证两端一致;不要在原帧格式中静默更改算法。
流式解析器应设置最大负载长度、按命令验证长度、等待完整帧、校验失败后重新寻找候选帧头,并在超时后丢弃未完成帧。负载内允许出现AA,因此不能遇到每个AA都重新开始。串口一次read()的长度不代表帧边界。
6. 开发、调试与验收
跳转到“6. 开发、调试与验收”原文列举STM32CubeIDE、Keil或CLion+CMake,以及ST-Link V2。它们是开发路径选项,不意味着本页已提供相应工程配置。若实际工程已有工具链文件、启动文件和链接脚本,可按其README构建;普通桌面CMake默认编译器不能生成STM32固件。
烧录命令仍可作为有条件的模板,路径必须对应实际产生的ELF:
openocd -f interface/stlink.cfg -f target/stm32f1x.cfg \ -c "program build/powershow.elf verify reset exit"原记录中的python tools/battery_monitor.py COM3也只有在该工具实际存在时才可运行。调试日志不应与二进制协议混用同一个UART字节流;可用独立串口、SWO或受控的日志帧。
| 检查 | 方法和通过条件 |
|---|---|
| I²C/SMBus不通 | 核对供电、电平、上拉、地址表示、重复起始和超时;示波器/逻辑分析仪确认ACK |
| 电池读数异常 | 对照型号手册检查字节序、符号、温度转换、容量模式和状态映射 |
| 串口不通 | 核对115200/8N1、TX/RX、电平、端口和驱动;先做隔离的链路回环 |
| 帧解析 | 黄金帧、逐字节输入、粘包、噪声、错误长度、坏校验、负载含AA和半帧超时 |
| 数据有效性 | 模拟掉线、NACK、PEC失败、重连和过期;故障不能显示成0%或继续刷新时间 |
| 调度与DMA | 模拟发送忙、队列满和毫秒计数回卷;缓冲未发送完不得重用 |
| SOC精度 | 与可溯源充放电测量比较,明确温度、负载、容量基准和误差定义 |
| 充电干扰 | 同一采集输入下比较电池供电/充电中的噪声谱和1X幅相,不能只看电压纹波 |
| 保护与热设计 | 独立验证实际保护与充电硬件,监测软件的告警不能替代保护动作 |
原分工为架构/集成、电池驱动、串口协议三项职责。原进度写“前三周完成、第四周测试”,但没有提交记录或测试证据,现作为实施计划保留:先冻结电池接口,再开发驱动与协议,随后联调,最后补压力测试与文档。原版本日志的2024-12-04与文章发布日期2025-12-04不一致,不能据此确认正式发布日。
原材料含“公司内部使用”的授权说明;它不构成开源许可证,也不代表相关完整工程可以公开复用。本页仅整理已提供笔记中的通用设计与示例,未附公司项目、联系占位信息或私有电池配置。
来源与本次核验
跳转到“来源与本次核验”原始项目记录:现场平衡仪电池管理系统。补充参考为上述ST、SBS、SMBus、NXP和TI原始资料,核对日期2026-10-03。
本次运行了正文Python编码/解码,并检查负电流、温度边界、无效状态、长度和坏校验;续航、温度转换和线速时间也作数值复算。没有取得实际powershow固件、电量计型号或电池包,因此不声称完成上板、SOC精度、充电干扰或保护验收。采集硬件的预算方法见便携式平衡仪采集架构。