简介:面向中颖SH367309锂电池保护板开发者的源码解析资料,适合从事嵌入式BMS、电池管理及量产固件开发的软件人员参考。资源共包含3个文件,以HTML说明页为主体,辅以inscode工程入口和gitignore配置,压缩包仅7KB,便于快速浏览代码结构与设计要点。解析采用自底向上的方式,从汇编启动、CMSIS核心文件到ADC、Flash模拟EEPROM、外设中断、键盘、LCD等驱动,并覆盖SMBus从机、电量计量与保护状态机,清晰呈现一套体积小于64kB、适合掩膜量产的寄存器版HAL工程。可靠性方面重点说明了双备份Flash、CRC16、掉电续写、硬件看门狗等机制,也指出ADC改用DMA双缓冲、SMBus增加PEC校验等可优化方向,有助于二次开发和移植。目前已有94人学习浏览,适合需要深入理解该芯片软件框架及可靠性设计的开发人员。 做嵌入式这几年,电池保护板相关的项目我接触了不少,但真正让我觉得值得写一篇源码解析的,还是这个基于中颖SH367309的项目。这颗芯片在中低端锂电池保护方案里出镜率很高,尤其在电动工具、吸尘器、储能电源这些多串锂电池场景下,属于典型的“功能集成度高、外围电路简单”的保护IC。拿到这套项目代码的时候,我第一反应是——保护逻辑这种东西,代码量通常不大,但真正把每一行和芯片内部机制对应起来,才是解析的重点。这篇就来从头到尾拆一遍这套源码,把核心模块、关键参数、调试心得一次说清楚。
1. 项目背景与代码整体设计
1.1 SH367309是什么,源码解决什么问题
SH367309是中颖推出的一颗多串锂电池保护IC,支持3到16串电池组,内部集成了高精度ADC、比较器、充电泵驱动以及均衡控制逻辑。它和传统分立方案最大的区别在于:保护阈值、延时时间、均衡策略这些参数,都可以通过I2C接口配置,而不是靠外部电阻硬调。这就意味着,整套保护逻辑的“大脑”其实在MCU侧——芯片负责采集电压和电流,MCU负责读取数据、判断状态、下发控制指令。
拿到这套源码时,我第一反应是——保护逻辑这种东西,产品形态再变,内核不外乎“采样—判断—动作”三个环节。但真正去读代码才发现,坑全藏在细节里:寄存器配置顺序、ADC数据的滤波方式、保护触发后的状态机迁移,甚至I2C读时序的稳定性,都会决定一块保护板是“正常工作”还是“偶尔抽风”。所以源码解析不是把代码贴出来念一遍,而是要把每段逻辑和芯片手册里的寄存器、时序图一一对应起来。
这套代码在项目中担任的角色是:完成SH367309的初始化配置、实时读取电池组电压和温度、执行过充/过放/过流/短路保护判断、控制均衡策略,并通过指示灯或通信接口上报状态。适合做电池保护板开发、BMS底层软件,以及想搞懂“保护IC+MCU如何配合”的嵌入式工程师参考。
1.2 代码目录结构与模块划分
项目代码整体采用了分层思路,虽然不是特别复杂的架构,但对于这类规模适中的嵌入式固件来说,分层清晰比炫技重要得多。目录结构大致如下:
app/ :应用层,包含主循环、状态机、保护策略逻辑
driver/ :芯片驱动层,主要是SH367309的寄存器读写与初始化
port/ :平台移植层,包括I2C底层接口、延时函数、串口打印等
middleware/ :中间层,比如滤波算法、数据校验、均衡控制算法
config/ :配置头文件,所有阈值参数、使能开关都集中在这里
说实话,第一次看到这个结构,我最满意的一点就是配置隔离做得好。所有需要根据电池规格调整的参数,比如过充电压、过放电压、过流阈值、均衡开启压差,全部集中在 config/battery_config.h 里,业务代码里几乎没有散落的魔数。这意味着换一个电芯规格,只需要改这个头文件,而不用动保护逻辑,工程实践上能省下大量排查时间。
2. 初始化流程与寄存器配置要点
2.1 上电初始化顺序
SH367309的初始化顺序是有讲究的。源码里第一步是I2C设备地址配置——这颗芯片的地址可以通过外围引脚拉高/拉低来设定,所以代码里没有把地址写死,而是从配置头文件里读取一个 SH367309_I2C_ADDR 宏。这里有个小陷阱:芯片手册上写的地址通常是7位地址,但I2C驱动读写时用的是8位地址(左移一位),定义宏的时候最好直接定义7位地址,在驱动层统一转换,否则容易在读写地址上错位。
初始化流程简化后大致如下:
延时等待芯片上电稳定(实测至少需要10ms以上,太短会读到异常的寄存器值)
读取芯片ID寄存器,确认通信正常
配置保护阈值寄存器组
配置延时时间寄存器组
配置均衡相关参数
写入CTRL寄存器,使能保护功能
回读所有配置数据,做一次校验
2.2 寄存器配置的方**
SH367309的寄存器配置有个特点:很多寄存器是“写1清0”或“按位生效”的,直接对整个寄存器赋值容易误操作。源码里专门做了 read-modify-write 的封装,也就是“先读回原始值,修改目标位,再写回”。这个封装看起来多余,实际调试时帮了大忙。我当时就踩过一次坑——直接对整个寄存器写入配置,结果把另一个位的保护功能给关了,电池过放到2.8V都没触发保护,吓得一身冷汗。
配置阈值参数时,源码里做了一个非常实用的抽象,把所有阈值统一换算成ADC原始值来进行配置,而不是直接使用电压值。原因是芯片内部的比较器和ADC天然工作在原始采样值域,比如过充阈值寄存器里存的不是“3.65V”,而是对应的ADC量化值。代码里的换算函数长这样:
uint16_t voltage_to_adc(uint16_t voltage_mv, uint16_t adc_full_scale_mv, uint16_t adc_resolution)
{
return (uint32_t)voltage_mv * adc_resolution / adc_full_scale_mv;
}
这个设计的好处是,当ADC参考电压因温度或其他因素发生漂移时,只需要调整 adc_full_scale_mv 这一个参数,所有阈值自动跟随校正,而不需要逐个寄存器去改。
3. 核心逻辑拆解与实现细节
3.1 数据采集与滤波处理
保护逻辑的判断基础是实时、准确的电压和电流数据。源码在采集部分没有直接使用芯片中断回调的单次采样值,而是做了一级滑动滤波。具体做法是维护一个长度为8的环形缓冲区,每次采样刷新后,去掉最大值和最小值,取中间6个数据的平均值。这个算法实现简单,但效果非常明显——特别是电动工具这类负载波动大的场景,瞬间的电压毛刺很容易造成误保护,滤波后才能准确反映电池真实状态。
代码实现上,这个滤波算法放在 middleware/filter.c 里,接口非常简洁:
uint16_t filter_get_avg_voltage(uint8_t channel, uint16_t new_sample)
{
// 将新采样值写入环形缓冲,去掉最大最小后取均值
}
采样周期的选择也值得说。源码里主循环的采样周期是10ms,但电压通道 和电流通道交替采样,实际每个通道的采样间隔是20ms。对于过压和欠压保护来说,20ms的响应速度足够快,同时也给了滤波算法足够的数据量来平滑抖动。如果采样过快,滤波反而会滞后于真实电压变化,这在动态负载下反而可能导致保护不及时。
3.2 保护状态机设计
过充、过放、过流、短路,这四类保护虽然触发条件不同,但源码中统一用了一个状态机来管理,避免“多个保护逻辑互相打架”的问题。状态机设计如下:
ST_IDLE :正常工作状态,所有保护未触发
ST_OVP :过充保护触发,等待恢复条件满足
ST_UVP :过放保护触发,等待恢复条件满足
ST_OCP :过流保护触发,等待恢复条件满足
ST_SCP :短路保护触发,需要硬件复位或手动恢复
状态迁移的核心在 app/bms_state.c 里,每次采样完成后执行一次状态评估。这里有一个很关键的细节:过充和过放的恢复条件不等同于触发条件的反向,而是留了滞回区间。例如过充触发电压是3.65V,但恢复电压设置成3.55V,避免电池电压在阈值附近来回抖动时,保护反复开启关闭,损坏MOS 管和电芯。
滞回参数同样放在配置文件里,两个独立宏定义:
#define CELL_OVP_THRESHOLD_MV 3650
#define CELL_OVP_RELEASE_MV 3550
3.3 短路保护:为什么走硬件中断
在四类保护里,短路保护最特殊,因为它对响应时间的要求是微秒级。MCU就算主频再高,走“采样—滤波—判断—控制MOS”这条链路也来不及。所以SH367309内部集成了硬件比较器,短路时由芯片直接切断MOS驱动器,同时在CTRL寄存器里置一个短路发生标志位。MCU的角色变成了“事后读取标志,记录事件,等待用户手动复位”。
源码里对这个标志位的处理值得学习:一旦检测到短路标志置位,先读取并保存完整的状态寄存器值,再执行一次软件复位清零标志。这样既保住了故障现场,又确保下次短路时标志还能正常置位。如果清零顺序反了,故障信息就丢了,排查问题时会很被动。
3.4 均衡策略:最简单的才是可靠的
均衡逻辑在源码里没有做得很复杂,用的是一种“阈值开启、电压差触发”的被动均衡策略。核心思路是:
每次采样后,计算所有电芯电压的最大值和最小值
如果最大压差超过 BALANCE_START_DELTA_MV (默认30mV),且最高电压高于 BALANCE_START_ABS_MV (默认3.4V),说明电芯间出现了明显不一致,开启均衡
均衡方式是对电压最高的那节电芯接通旁路放电电阻,降低它的电压
当压差回落到 BALANCE_STOP_DELTA_MV (默认15mV)以下,关闭均衡
这里加一个“绝对电压”条件非常关键。想象一下整组电池都在3.0V以下时,电芯间有20mV的压差——这时候开启均衡去放最高节电芯的电,不仅没法让电池组容量变高,还会加速欠压,把好电芯也拖垮。加了这个 BALANCE_START_ABS_MV 门槛,就确保了均衡只在接近满电区域执行,这才是实际有效果的均衡窗口。
4. 调试阶段踩过的坑与排查实录
4.1 I2C通信异常:示波器才是最终裁判
项目调试初期遇到最头疼的,是I2C读取到的电压数据偶发性跳变。有时候读回来的值是合理的,有时候突然冒出来一个0V或者满量程值。代码逻辑排查了好几遍,滤波算法也没发现问题,最后用示波器抓I2C波形才发现——是上拉电阻阻值选大了,SCL上升沿特别缓,在高速读操作时偶发时钟毛刺导致数据错位。
SH367309的数据手册里对I2C时序要求比较严格,源码里虽然配置了400kHz速率,但实际跑起来发现外部上拉电阻用4.7k不够,换成2.2k后波形立刻干净了。这个经验适用于大部分I2C设备:如果遇到偶发通信错误,先别怀疑代码,用示波器看波形,重点检查边沿时间和毛刺。
4.2 过放保护恢复后的“假死”现象
另一个印象深刻的坑是:电池组触发过放保护后,接上充电器,发现设备没有任何反应。代码里过放恢复条件是“检测到充电电压且电芯电压高于恢复阈值”,但SH367309在过放保护后,内部MOS会被关断,此时电池包对外相当于断路,充电器插入时根本不会被系统检测到。
排查后发现,芯片有一个“充电检测唤醒”功能,需要MCU在初始化时使能对应寄存器位。这个功能的作用是:当充电器插入时,通过外部电压唤醒芯片内部的充电检测电路,从而重新闭合MOS管,让MCU有机会继续运行并完成状态恢复。源码里很早就配置了这个位,但默认值是不启用的,如果参照手册某个旧版本示例代码,很容易漏掉这个配置。
4.3 常见问题速查表
整理一份我在实际调试中遇到过的问题和对应解法,供大家排查时参考:
4.4 源码级避坑技巧
最后分享几个我在这套代码里沉淀下来的调试技巧,常规文档里基本不会写。
第一,芯片所有阈值寄存器都是16位宽,但不同地址的寄存器,有效位数和LSB权重不一样。配置前先查手册确认每个寄存器的“步进值”,比如过充阈值寄存器每个LSB是10mV,过流阈值可能是1A甚至更大。直接用十六进制去推阈值,十有**会出错。我的习惯是在配置文件里把所有参数都用“真实物理值”定义,比如 3500mV ,然后通过宏换算成寄存器写入值,代码自动保证单位和精度。
第二,调试保护逻辑时,故意用可调电源替代真实电池组。把过充阈值调低到比如4.0V,再用可调电源模拟电压上升到4.1V,可以快速复现保护动作,而不需要真的把电池充到过充状态。这套手段能大幅压缩调试周期,也安全得多。
第三,给所有保护事件打时间戳和状态快照。这套源码里每个保护事件触发时,会把当时的电芯电压、电流、温度、状态寄存器值一起写入一个日志结构体。前期调试可能觉得麻烦,等整机测试出问题时,你才会意识到这是性价比最高的排查手段。
5. 源码移植与扩展建议
5.1 换芯片型号要注意什么
如果你准备把这套代码移植到SH367309同系列的其他型号,比如不同串数对应的版本,最需要关注的是ADC通道数量和寄存器地址偏移。不同封装和不同串数版本,内部ADC通道数会不同,寄存器地址也可能整体偏移。移植时,先对照新芯片手册,重点核对以这几部分:
I2C设备地址和时序是否一致
各通道ADC寄存器地址
保护阈值寄存器的LSB权重
均衡控制的通道映射
5.2 代码风格与工程习惯建议
读这套代码的过程中,最值得借鉴的是它的“配置集中化”思路。所有与硬件相关的参数、阈值、使能开关都放在配置头文件里,业务代码只通过宏来引用,不直接出现裸的字面常量。这样做的好处,除了换电芯规格时改一个文件就能完成适配外,更重要的是,当多个工程师协作开发时,不会出现“这个阈值是在哪个文件里面改”的沟通成本。
另外一点是,这套代码对错误处理的态度比较务实——I2C读通信失败时,不会中断系统,而是连续失败一定次数后才选择重启初始化或置一个错误标志。对于电池保护应用来说,一次通信失败完全可能是外部干扰导致,直接宕机反而会让系统失去保护能力。做个简单重试机制,比费尽精力保证每次通信都成功,要实际得多。
如果你想把这套代码用在自己的项目里,我建议先从 config/battery_config.h 入手,对照你手头电池组的规格书,把阈值参数理清楚,再跑一遍初始化流程确认寄存器回读值正确。保护逻辑本身代码量不大,读懂状态机和采样部分之后,基于产品定制改动也就是一两个文件的事。
————————————————
版权声明:本文为CSDN博主「芳奎」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_30036289/article/details/164244368
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?注册
×
|